/** * Copyright (c) 2009 International Health Terminology Standards Development * Organisation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * Copyright CSIRO Australian e-Health Research Centre (http://aehrc.com). * All rights reserved. Use is subject to license terms and conditions. */ package au.csiro.snorocket.core.util; /** * Concepts are integers >= 0 * * @author law223 */ public final class FastConceptHashSet implements IConceptSet { /** * Serialisation version. */ private static final long serialVersionUID = 1L; private static final int TOMBSTOMB = -2; private static final int EMPTY = -1; private final static int REPROBE_LIMIT = 10; private int[] _keys; private int _size; public FastConceptHashSet() { clear(); } /** * @param size * must be a power of 2 */ private void reallocate(final int size) { _keys = new int[size]; java.util.Arrays.fill(_keys, EMPTY); _size = 0; } public void add(final int concept) { final int len = _keys.length; final int mask = len - 1; int reprobe_count = 0; int idx = concept & mask; while (true) { final int K = _keys[idx]; if (K < 0) { // K == EMPTY || K == TOMBSTONE _keys[idx] = concept; _size++; return; } if (concept == K) { return; } if (++reprobe_count > reprobeLimit(len)) { break; } idx = (idx + 1) & mask; } resize(); add(concept); } private void resize() { grow(_keys.length * 2); // System.err.println(hashCode() + " resize from " + items.length); } public void addAll(IConceptSet set) { for (final IntIterator itr = set.iterator(); itr.hasNext();) { add(itr.next()); } } public void clear() { reallocate(1); } public boolean contains(int concept) { final int len = _keys.length; final int mask = len - 1; int reprobe_count = 0; int idx = concept & mask; while (true) { final int K = _keys[idx]; if (K == EMPTY) { return false; } if (concept == K) { return true; } if (++reprobe_count > reprobeLimit(len)) { break; } idx = (idx + 1) & mask; } return false; } private int reprobeLimit(final int len) { return REPROBE_LIMIT + (len >> 2); } public boolean containsAll(IConceptSet concepts) { throw new UnsupportedOperationException(); } public boolean isEmpty() { return 0 == _size; } public IntIterator iterator() { if (_size < (_keys.length >> 1)) { // System.err.println("Shrinking from " + _keys.length); final int[] oldItems = _keys; reallocate(_keys.length >> 1); for (int i = 0; i < oldItems.length; i++) { final int concept = oldItems[i]; if (concept >= 0) { // concept != EMPTY && concept != TOMBSTONE add(concept); } } } return new IntIterator() { int next = 0; public boolean hasNext() { while (next < _keys.length && _keys[next] < 0) { next++; } return next < _keys.length; } public int next() { return hasNext() ? _keys[next++] : EMPTY; } }; } public void remove(final int concept) { final int len = _keys.length; final int mask = _keys.length - 1; int reprobe_count = 0; int idx = concept & mask; while (true) { final int K = _keys[idx]; if (EMPTY == K) { return; } if (concept == K) { _size--; _keys[idx] = TOMBSTOMB; } if (++reprobe_count > reprobeLimit(len)) { break; } idx = (idx + 1) & mask; } } public void removeAll(IConceptSet set) { if (size() < set.size()) { // Can't use iterator since we're going to modify the set for (int i = 0; i < _keys.length; i++) { final int concept = _keys[i]; if (concept >= 0 && set.contains(concept)) { _keys[i] = TOMBSTOMB; _size--; } } } else { for (final IntIterator itr = set.iterator(); itr.hasNext();) { final int concept = itr.next(); remove(concept); } } } public int size() { return _size; } @Override public String toString() { final StringBuilder sb = new StringBuilder(); sb.append("{"); for (final IntIterator itr = iterator(); itr.hasNext();) { final int c = itr.next(); sb.append(c); if (itr.hasNext()) { sb.append(", "); } } sb.append("}"); return sb.toString(); } public void grow(int newSize) { final int[] oldItems = _keys; reallocate(newSize); for (int i = 0; i < oldItems.length; i++) { final int concept = oldItems[i]; if (concept >= 0) { // concept != EMPTY && concept != TOMBSTONE add(concept); } } } public int[] toArray() { throw new UnsupportedOperationException(); } }