package speiger.src.collections.PACKAGE.maps.impl.concurrent; import java.util.Arrays; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.concurrent.locks.StampedLock; import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.BiFunction; #if !TYPE_OBJECT && JDK_TYPE import java.util.function.PREDICATE; #endif #if !SAME_TYPE && JDK_VALUE && !VALUE_OBJECT import java.util.function.VALUE_PREDICATE; #endif #if !TYPE_OBJECT import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; #if SAME_TYPE import speiger.src.collections.PACKAGE.collections.ITERATOR; #endif import speiger.src.collections.PACKAGE.functions.CONSUMER; import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; #endif import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; #if !TYPE_OBJECT && !VALUE_OBJECT import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; #endif #if !SAME_TYPE && !TYPE_INT import speiger.src.collections.ints.functions.consumer.VALUE_BI_FROM_INT_CONSUMER; #endif #if !TYPE_INT || !SAME_TYPE import speiger.src.collections.PACKAGE.functions.consumer.BI_CONSUMER; #endif #if !VALUE_BOOLEAN || !JDK_TYPE import speiger.src.collections.PACKAGE.functions.function.FUNCTION; #endif import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; #if !SAME_TYPE import speiger.src.collections.PACKAGE.functions.function.SINGLE_UNARY_OPERATOR; #endif #if !TYPE_OBJECT && !VALUE_BOOLEAN && !JDK_TYPE import speiger.src.collections.PACKAGE.functions.function.PREDICATE; #endif import speiger.src.collections.PACKAGE.maps.abstracts.ABSTRACT_MAP; import speiger.src.collections.PACKAGE.maps.interfaces.MAP; import speiger.src.collections.PACKAGE.maps.interfaces.CONCURRENT_MAP; #if !TYPE_OBJECT import speiger.src.collections.PACKAGE.sets.ABSTRACT_SET; import speiger.src.collections.PACKAGE.sets.SET; #endif import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_ABSTRACT_COLLECTION; import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_COLLECTION; import speiger.src.collections.VALUE_PACKAGE.functions.VALUE_SUPPLIER; #if !SAME_TYPE import speiger.src.collections.VALUE_PACKAGE.functions.function.VALUE_UNARY_OPERATOR; #if !VALUE_OBJECT #if !TYPE_OBJECT import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; #endif import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_ITERATOR; import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_BI_ITERATOR; import speiger.src.collections.VALUE_PACKAGE.functions.VALUE_CONSUMER; #endif #else if !VALUE_OBJECT import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; #endif #if !TYPE_OBJECT && !VALUE_OBJECT || !VALUE_OBJECT import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; #endif #if !SAME_TYPE #if !TYPE_OBJECT import speiger.src.collections.objects.functions.consumer.VALUE_BI_FROM_OBJECT_CONSUMER; #endif #if !JDK_VALUE import speiger.src.collections.VALUE_PACKAGE.functions.function.VALUE_PREDICATE; #endif #endif #if VALUE_OBJECT import speiger.src.collections.objects.collections.ObjectIterator; #endif import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; import speiger.src.collections.objects.sets.AbstractObjectSet; import speiger.src.collections.objects.sets.ObjectSet; import speiger.src.collections.utils.HashUtil; import speiger.src.collections.utils.ITrimmable; /** * A TypeSpecific ConcurrentHashMap implementation that is based on Guavas approach and backing array implementations. * Like Guavas implementation this solution can be accessed by multiple threads, but it is not as flexible as Javas implementation. * The concurrencyLevel decides how many pools exist, and each pool can be accessed by 1 thread for writing and as many threads for reading. * Though it is ill adviced to iterate over the collection using the Iterator if the Map is written to. Keep that in mind. * * * @Type(T) * @ValueType(V) */ public class CONCURRENT_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENERIC_TYPE implements CONCURRENT_MAP KEY_VALUE_GENERIC_TYPE, ITrimmable { /** Segment Limit */ private static final int MAX_SEGMENTS = 1 << 16; /** Buckets of the ConcurrentMap */ protected transient Segment KEY_VALUE_GENERIC_TYPE[] segments; /** Bitshift of the HashCode */ protected transient int segmentShift; /** Max Bits thats used in the segments */ protected transient int segmentMask; /** EntrySet cache */ protected transient FastEntrySet KEY_VALUE_GENERIC_TYPE entrySet; /** KeySet cache */ protected transient SET KEY_GENERIC_TYPE keySet; /** Values cache */ protected transient VALUE_COLLECTION VALUE_GENERIC_TYPE values; /** * Copy constructor that doesn't trigger the building of segments and allows to copy it faster. * @param unused not used, Just to keep all constructors accessible. */ protected CONCURRENT_HASH_MAP(boolean unused) {} /** * Default Constructor */ public CONCURRENT_HASH_MAP() { this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * Constructor that defines the minimum capacity * @param minCapacity the minimum capacity the HashMap is allowed to be. * @throws IllegalStateException if the minimum capacity is negative */ public CONCURRENT_HASH_MAP(int minCapacity) { this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * Constructor that defines the minimum capacity and load factor * @param minCapacity the minimum capacity the HashMap is allowed to be. * @param loadFactor the percentage of how full the backing array can be before they resize * @throws IllegalStateException if the minimum capacity is negative * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 */ public CONCURRENT_HASH_MAP(int minCapacity, float loadFactor) { this(minCapacity, loadFactor, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * Constructor that defines the minimum capacity and concurrencyLevel * @param minCapacity the minimum capacity the HashMap is allowed to be. * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the minimum capacity is negative * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(int minCapacity, int concurrencyLevel) { this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, concurrencyLevel); } /** * Constructor that defines the load factor and concurrencyLevel * @param loadFactor the percentage of how full the backing array can be before they resize * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(float loadFactor, int concurrencyLevel) { this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, concurrencyLevel); } /** * Constructor that defines the minimum capacity, load factor and concurrencyLevel * @param minCapacity the minimum capacity the HashMap is allowed to be. * @param loadFactor the percentage of how full the backing array can be before they resize * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the minimum capacity is negative * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(int minCapacity, float loadFactor, int concurrencyLevel) { if(minCapacity < 0) throw new IllegalStateException("Minimum Capacity is negative. This is not allowed"); if(loadFactor <= 0 || loadFactor >= 1F) throw new IllegalStateException("Load Factor is not between 0 and 1"); if(concurrencyLevel <= 0 || concurrencyLevel >= MAX_SEGMENTS) throw new IllegalStateException("concurrencyLevel has to be between 0 and 65536"); int segmentCount = HashUtil.nextPowerOfTwo(concurrencyLevel); int shift = Integer.numberOfTrailingZeros(segmentCount); segments = new Segment[segmentCount]; segmentShift = 32 - shift; segmentMask = segmentCount - 1; int segmentCapacity = minCapacity / segmentCount; if(segmentCapacity * segmentCount < minCapacity) { segmentCapacity++; } segmentCapacity = HashUtil.arraySize(segmentCapacity, loadFactor); for(int i = 0;i map) { this(map, HashUtil.DEFAULT_LOAD_FACTOR, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * A Helper constructor that allows to create a Map with exactly the same values as the provided map. * @param map the values that should be present in the map * @param loadFactor the percentage of how full the backing array can be before they resize * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 */ public CONCURRENT_HASH_MAP(Map map, float loadFactor) { this(map, loadFactor, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * A Helper constructor that allows to create a Map with exactly the same values as the provided map. * @param map the values that should be present in the map * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(Map map, int concurrencyLevel) { this(map, HashUtil.DEFAULT_LOAD_FACTOR, concurrencyLevel); } /** * A Helper constructor that allows to create a Map with exactly the same values as the provided map. * @param map the values that should be present in the map * @param loadFactor the percentage of how full the backing array can be before they resize * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(Map map, float loadFactor, int concurrencyLevel) { this(map.size(), loadFactor, concurrencyLevel); putAll(map); } /** * A Type Specific Helper function that allows to create a new Map with exactly the same values as the provided map. * @param map the values that should be present in the map */ public CONCURRENT_HASH_MAP(MAP KEY_VALUE_GENERIC_TYPE map) { this(map, HashUtil.DEFAULT_LOAD_FACTOR, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * A Type Specific Helper function that allows to create a new Map with exactly the same values as the provided map. * @param map the values that should be present in the map * @param loadFactor the percentage of how full the backing array can be before they resize * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 */ public CONCURRENT_HASH_MAP(MAP KEY_VALUE_GENERIC_TYPE map, float loadFactor) { this(map, loadFactor, HashUtil.DEFAULT_MIN_CONCURRENCY); } /** * A Type Specific Helper function that allows to create a new Map with exactly the same values as the provided map. * @param map the values that should be present in the map * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(MAP KEY_VALUE_GENERIC_TYPE map, int concurrencyLevel) { this(map, HashUtil.DEFAULT_LOAD_FACTOR, concurrencyLevel); } /** * A Type Specific Helper function that allows to create a new Map with exactly the same values as the provided map. * @param map the values that should be present in the map * @param loadFactor the percentage of how full the backing array can be before they resize * @param concurrencyLevel decides how many operations can be performed at once. * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 * @throws IllegalStateException if the concurrencyLevel is either below/equal to 0 or above/equal to 65535 */ public CONCURRENT_HASH_MAP(MAP KEY_VALUE_GENERIC_TYPE map, float loadFactor, int concurrencyLevel) { this(map.size(), loadFactor, concurrencyLevel); putAll(map); } @Override public VALUE_TYPE put(KEY_TYPE key, VALUE_TYPE value) { int hash = getHashCode(key); return getSegment(hash).put(hash, key, value); } @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { int hash = getHashCode(key); return getSegment(hash).putIfAbsent(hash, key, value); } #if VALUE_PRIMITIVES @Override public VALUE_TYPE addTo(KEY_TYPE key, VALUE_TYPE value) { int hash = getHashCode(key); return getSegment(hash).addTo(hash, key, value); } @Override public VALUE_TYPE subFrom(KEY_TYPE key, VALUE_TYPE value) { int hash = getHashCode(key); return getSegment(hash).subFrom(hash, key, value); } #endif @Override public VALUE_TYPE REMOVE_VALUE(KEY_TYPE key) { int hash = getHashCode(key); return getSegment(hash).remove(hash, key); } #if !TYPE_OBJECT || !VALUE_OBJECT @Override public boolean remove(KEY_TYPE key, VALUE_TYPE value) { int hash = getHashCode(key); return getSegment(hash).remove(hash, key, value); } #endif @Override public boolean remove(Object key, Object value) { int hash = getHashCode(key); return getSegment(hash).remove(hash, key, value); } @Override public VALUE_TYPE REMOVE_VALUEOrDefault(KEY_TYPE key, VALUE_TYPE defaultValue) { int hash = getHashCode(key); return getSegment(hash).removeOrDefault(hash, key, defaultValue); } @Override public VALUE_TYPE GET_VALUE(KEY_TYPE key) { int hash = getHashCode(key); return getSegment(hash).get(hash, key); } @Override public CLASS_VALUE_TYPE get(Object key) { int hash = getHashCode(key); return VALUE_TO_OBJ(getSegment(hash).get(hash, key)); } #if TYPE_OBJECT && VALUE_OBJECT @Override public VALUE_TYPE getOrDefault(Object key, VALUE_TYPE defaultValue) { int hash = getHashCode(key); return getSegment(hash).getOrDefault(hash, key, defaultValue); } #else @Override public VALUE_TYPE getOrDefault(KEY_TYPE key, VALUE_TYPE defaultValue) { int hash = getHashCode(key); return getSegment(hash).getOrDefault(hash, key, defaultValue); } #endif @Override public void forEach(BI_CONSUMER KEY_VALUE_GENERIC_TYPE action) { for(int i = 0,m=segments.length;i 0) return false; } return true; } @Override public int size() { long size = 0L; for(int i = 0,m=segments.length;i Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)size; } @Override public ObjectSet ENTRY_SET() { if(entrySet == null) entrySet = new MapEntrySet(); return entrySet; } @Override public SET KEY_GENERIC_TYPE keySet() { if(keySet == null) keySet = new KeySet(); return keySet; } @Override public VALUE_COLLECTION VALUE_GENERIC_TYPE values() { if(values == null) values = new Values(); return values; } protected int getSegmentIndex(int hash) { return (hash >>> segmentShift) & segmentMask; } protected Segment KEY_VALUE_GENERIC_TYPE getSegment(int hash) { return segments[(hash >>> segmentShift) & segmentMask]; } #if !TYPE_OBJECT protected int getHashCode(KEY_TYPE key) { return HashUtil.mix(KEY_TO_HASH(key)); } #endif protected int getHashCode(Object obj) { return HashUtil.mix(Objects.hashCode(obj)); } private class MapEntrySet extends AbstractObjectSet implements MAP.FastEntrySet KEY_VALUE_GENERIC_TYPE { @Override public ObjectBidirectionalIterator iterator() { return new EntryIterator(); } @Override public ObjectBidirectionalIterator fastIterator() { return new FastEntryIterator(); } @Override public MapEntrySet copy() { throw new UnsupportedOperationException(); } @Override public void forEach(Consumer action) { for(int i = 0,m=segments.length;i action) { MapEntry entry = new MapEntry(); for(int i = 0,m=segments.length;i action) { Objects.requireNonNull(action); int count = 0; for(int i = 0,m=segments.length;i void forEach(E input, ObjectObjectConsumer action) { Objects.requireNonNull(action); for(int i = 0,m=segments.length;i filter) { Objects.requireNonNull(filter); MapEntry entry = new MapEntry(); for(int i = 0,m=segments.length;i filter) { Objects.requireNonNull(filter); MapEntry entry = new MapEntry(); for(int i = 0,m=segments.length;i filter) { Objects.requireNonNull(filter); MapEntry entry = new MapEntry(); for(int i = 0,m=segments.length;i E reduce(E identity, BiFunction operator) { Objects.requireNonNull(operator); E state = identity; for(int i = 0,m=segments.length;i operator) { Objects.requireNonNull(operator); MAP.Entry KEY_VALUE_GENERIC_TYPE state = null; boolean empty = true; for(int i = 0,m=segments.length;i filter) { Objects.requireNonNull(filter); MapEntry entry = new MapEntry(); for(int i = 0,m=segments.length;i filter) { Objects.requireNonNull(filter); int result = 0; MapEntry entry = new MapEntry(); for(int i = 0,m=segments.length;i= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), seg.values[index]); } finally { seg.unlockRead(stamp); } } else { Map.Entry entry = (Map.Entry)o; int hash = getHashCode(entry.getKey()); Segment KEY_VALUE_GENERIC_TYPE seg = getSegment(hash); long stamp = seg.readLock(); try { int index = seg.findIndex(hash, entry.getKey()); if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(seg.values[index])); } finally { seg.unlockRead(stamp); } } } return false; } @Override @Deprecated public boolean remove(Object o) { if(o instanceof Map.Entry) { if(o instanceof MAP.Entry) { MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; return CONCURRENT_HASH_MAP.this.remove(entry.ENTRY_KEY(), entry.ENTRY_VALUE()); } Map.Entry entry = (Map.Entry)o; return CONCURRENT_HASH_MAP.this.remove(entry.getKey(), entry.getValue()); } return false; } @Override public int size() { return CONCURRENT_HASH_MAP.this.size(); } @Override public void clear() { CONCURRENT_HASH_MAP.this.clear(); } } private final class KeySet extends ABSTRACT_SET KEY_GENERIC_TYPE implements SET KEY_GENERIC_TYPE { @Override public boolean add(KEY_TYPE key) { throw new UnsupportedOperationException(); } #if TYPE_OBJECT @Override @Deprecated public boolean contains(Object e) { return containsKey(e); } @Override public boolean remove(Object o) { int oldSize = size(); CONCURRENT_HASH_MAP.this.remove(o); return size() != oldSize; } #else @Override public boolean contains(KEY_TYPE e) { return containsKey(e); } @Override public boolean remove(KEY_TYPE o) { int oldSize = size(); CONCURRENT_HASH_MAP.this.remove(o); return size() != oldSize; } #endif @Override public BI_ITERATOR KEY_GENERIC_TYPE iterator() { return new KeyIterator(); } @Override public KeySet copy() { throw new UnsupportedOperationException(); } @Override public int size() { return CONCURRENT_HASH_MAP.this.size(); } @Override public void clear() { CONCURRENT_HASH_MAP.this.clear(); } @Override public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { Objects.requireNonNull(action); for(int i = 0,m=segments.length;i void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { Objects.requireNonNull(action); for(int i = 0,m=segments.length;i KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { Objects.requireNonNull(operator); KEY_SPECIAL_TYPE state = identity; for(int i = 0,m=segments.length;i void forEach(E input, VALUE_BI_FROM_OBJECT_CONSUMER VSV_GENERIC_TYPE action) { Objects.requireNonNull(action); for(int i = 0,m=segments.length;i VALUE_SPECIAL_TYPE reduce(VALUE_SPECIAL_TYPE identity, BiFunction operator) { Objects.requireNonNull(operator); VALUE_SPECIAL_TYPE state = identity; for(int i = 0,m=segments.length;i { MapEntry entry = new MapEntry(); public FastEntryIterator() {} @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { entry.set(nextEntry(), currentSegment()); return entry; } @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE previous() { entry.set(previousEntry(), currentSegment()); return entry; } } private class EntryIterator extends MapIterator implements ObjectBidirectionalIterator { MapEntry entry; public EntryIterator() {} @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { return entry = new ValueMapEntry(nextEntry(), currentSegment()); } @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE previous() { return entry = new ValueMapEntry(previousEntry(), currentSegment()); } @Override public void remove() { super.remove(); entry.clear(); } } private class ValueIterator extends MapIterator implements VALUE_BI_ITERATOR VALUE_GENERIC_TYPE { public ValueIterator() {} @Override public VALUE_TYPE VALUE_PREVIOUS() { return entry(previousEntry(), currentSegment()); } @Override public VALUE_TYPE VALUE_NEXT() { return entry(nextEntry(), currentSegment()); } protected VALUE_TYPE entry(int entry, int segment) { return segments[segment].values[entry]; } } private class KeyIterator extends MapIterator implements BI_ITERATOR KEY_GENERIC_TYPE { public KeyIterator() {} @Override public KEY_TYPE PREVIOUS() { return entry(previousEntry(), currentSegment()); } @Override public KEY_TYPE NEXT() { return entry(nextEntry(), currentSegment()); } protected KEY_TYPE entry(int entry, int segment) { return segments[segment].keys[entry]; } } private class MapIterator { int previous = -1; int next = -1; int current = -1; int previousSegment = -1; int nextSegment = -1; int currentSegment = -1; MapIterator() { currentSegment = getFirstSegment(); if(currentSegment != -1) next = segments[currentSegment].firstIndex; } public boolean hasNext() { return next != -1 || nextSegment != -1; } public boolean hasPrevious() { return previous != -1 || previousSegment != -1; } public int currentSegment() { return currentSegment; } public int previousEntry() { if(!hasPrevious()) throw new NoSuchElementException(); if(previousSegment != -1) { nextSegment = currentSegment; currentSegment = previousSegment; previousSegment = -1; next = current = segments[currentSegment].lastIndex; } else { if(next != -1) nextSegment = -1; next = current = previous; } findPreviousIndex(); return current; } public int nextEntry() { if(!hasNext()) throw new NoSuchElementException(); if(nextSegment != -1) { previousSegment = currentSegment; currentSegment = nextSegment; nextSegment = -1; previous = current = segments[currentSegment].firstIndex; } else { if(previous != -1) previousSegment = -1; previous = current = next; } findNextIndex(); return current; } public void remove() { if(current == -1) throw new IllegalStateException(); Segment KEY_VALUE_GENERIC_TYPE seg = segments[currentSegment]; long stamp = seg.writeLock(); try { if(current == previous) findPreviousIndex(); else findNextIndex(); seg.size--; if(previous == -1) seg.firstIndex = next; else seg.links[previous] ^= ((seg.links[previous] ^ (next & 0xFFFFFFFFL)) & 0xFFFFFFFFL); if(next == -1) seg.lastIndex = previous; else seg.links[next] ^= ((seg.links[next] ^ ((previous & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); if(current == seg.nullIndex) { current = -1; seg.containsNull = false; seg.keys[seg.nullIndex] = EMPTY_KEY_VALUE; seg.values[seg.nullIndex] = EMPTY_VALUE; } else { int slot, last, startPos = current; current = -1; KEY_TYPE current; while(true) { startPos = ((last = startPos) + 1) & seg.mask; while(true){ if(KEY_EQUALS_NULL((current = seg.keys[startPos]))) { seg.keys[last] = EMPTY_KEY_VALUE; seg.values[last] = EMPTY_VALUE; return; } slot = HashUtil.mix(KEY_TO_HASH(current)) & seg.mask; if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; startPos = ++startPos & seg.mask; } seg.keys[last] = current; seg.values[last] = seg.values[startPos]; if(next == startPos) next = last; if(previous == startPos) previous = last; seg.onNodeMoved(startPos, last); } } } finally { seg.unlockWrite(stamp); } } protected void findPreviousIndex() { previous = (int)(segments[currentSegment].links[current] >>> 32); if(previous == -1) { previousSegment = findPreviousSegment(currentSegment-1); } } protected void findNextIndex() { next = (int)(segments[currentSegment].links[current]); if(next == -1) { nextSegment = findNextSegment(currentSegment+1); } } private int getFirstSegment() { for(int i = 0,m=segments.length;i= segments.length ? -1 : index; } private int findPreviousSegment(int index) { for(;index >= 0 && segments[index].lastIndex == -1;index--); return index >= 0 ? index : -1; } } protected class ValueMapEntry extends MapEntry { protected KEY_TYPE key; protected VALUE_TYPE value; public ValueMapEntry(int index, int segmentIndex) { super(index, segmentIndex); key = segments[segmentIndex].keys[index]; value = segments[segmentIndex].values[index]; } @Override public KEY_TYPE ENTRY_KEY() { return key; } @Override public VALUE_TYPE ENTRY_VALUE() { return value; } @Override public VALUE_TYPE setValue(VALUE_TYPE value) { this.value = value; return super.setValue(value); } } protected class MapEntry implements MAP.Entry KEY_VALUE_GENERIC_TYPE, Map.Entry { int index = -1; int segmentIndex = -1; public MapEntry() {} public MapEntry(int index, int segmentIndex) { set(index, segmentIndex); } public void set(int index, int segmentIndex) { this.index = index; this.segmentIndex = segmentIndex; } public void clear() { index = -1; segmentIndex = -1; } @Override public KEY_TYPE ENTRY_KEY() { return segments[segmentIndex].keys[index]; } @Override public VALUE_TYPE ENTRY_VALUE() { return segments[segmentIndex].values[index]; } @Override public VALUE_TYPE setValue(VALUE_TYPE value) { Segment KEY_VALUE_GENERIC_TYPE seg = segments[segmentIndex]; long stamp = seg.writeLock(); try { VALUE_TYPE oldValue = seg.values[index]; seg.values[index] = value; return oldValue; } finally { seg.unlockWrite(stamp); } } @Override public boolean equals(Object obj) { if(obj instanceof Map.Entry) { if(obj instanceof MAP.Entry) { MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)obj; return KEY_EQUALS(ENTRY_KEY(), entry.ENTRY_KEY()) && VALUE_EQUALS(ENTRY_VALUE(), entry.ENTRY_VALUE()); } Map.Entry entry = (Map.Entry)obj; Object key = entry.getKey(); Object value = entry.getValue(); #if TYPE_OBJECT && VALUE_OBJECT return KEY_EQUALS(ENTRY_KEY(), key) && VALUE_EQUALS(ENTRY_VALUE(), value); #else if TYPE_OBJECT return value instanceof CLASS_VALUE_TYPE && KEY_EQUALS(ENTRY_KEY(), key) && VALUE_EQUALS(ENTRY_VALUE(), CLASS_TO_VALUE(value)); #else if VALUE_OBJECT return key instanceof CLASS_TYPE && KEY_EQUALS(ENTRY_KEY(), CLASS_TO_KEY(key)) && VALUE_EQUALS(ENTRY_VALUE(), value); #else return key instanceof CLASS_TYPE && value instanceof CLASS_VALUE_TYPE && KEY_EQUALS(ENTRY_KEY(), CLASS_TO_KEY(key)) && VALUE_EQUALS(ENTRY_VALUE(), CLASS_TO_VALUE(value)); #endif } return false; } @Override public int hashCode() { return KEY_TO_HASH(ENTRY_KEY()) ^ VALUE_TO_HASH(ENTRY_VALUE()); } @Override public String toString() { return KEY_TO_STRING(ENTRY_KEY()) + "=" + VALUE_TO_STRING(ENTRY_VALUE()); } } protected static class Segment KEY_VALUE_GENERIC_TYPE extends StampedLock { private static final long serialVersionUID = -446894977795760975L; protected final CONCURRENT_HASH_MAP KEY_VALUE_GENERIC_TYPE map; /** The Backing keys array */ protected transient KEY_TYPE[] keys; /** The Backing values array */ protected transient VALUE_TYPE[] values; /** The Backing array for links between nodes. Left 32 Bits => Previous Entry, Right 32 Bits => Next Entry */ protected transient long[] links; /** The First Index in the Map */ protected int firstIndex = -1; /** The Last Index in the Map */ protected int lastIndex = -1; /** If a null value is present */ protected transient boolean containsNull; /** Index of the Null Value */ protected transient int nullIndex; /** Maximum amount of Values that can be stored before the array gets expanded usually 75% */ protected transient int maxFill; /** Max Index that is allowed to be searched through nullIndex - 1 */ protected transient int mask; /** Amount of Elements stored in the HashMap */ protected int size; /** Minimum array size the Segment will be */ protected transient int minCapacity; /** How full the Arrays are allowed to get before resize */ protected float loadFactor; protected Segment(CONCURRENT_HASH_MAP KEY_VALUE_GENERIC_TYPE map) { this.map = map; } protected Segment(CONCURRENT_HASH_MAP KEY_VALUE_GENERIC_TYPE map, int minCapacity, float loadFactor, boolean isNullContainer) { this.map = map; this.minCapacity = minCapacity; this.loadFactor = loadFactor; mask = minCapacity - 1; maxFill = Math.min((int)Math.ceil(minCapacity * loadFactor), minCapacity - 1); nullIndex = isNullContainer ? minCapacity : -1; int arraySize = minCapacity + (isNullContainer ? 1 : 0); keys = NEW_KEY_ARRAY(arraySize); values = NEW_VALUE_ARRAY(arraySize); links = new long[arraySize]; } protected Segment KEY_VALUE_GENERIC_TYPE copy(CONCURRENT_HASH_MAP KEY_VALUE_GENERIC_TYPE newMap) { long stamp = readLock(); try { Segment KEY_VALUE_GENERIC_TYPE copy = new SegmentKV_BRACES(newMap); copy.keys = Arrays.copyOf(keys, keys.length); copy.values = Arrays.copyOf(values, values.length); copy.links = Arrays.copyOf(links, links.length); copy.firstIndex = firstIndex; copy.lastIndex = lastIndex; copy.containsNull = containsNull; copy.nullIndex = nullIndex; copy.maxFill = maxFill; copy.mask = mask; copy.size = size; copy.minCapacity = minCapacity; copy.loadFactor = loadFactor; return copy; } finally { unlockRead(stamp); } } protected VALUE_TYPE getDefaultReturnValue() { return map.getDefaultReturnValue(); } protected VALUE_TYPE put(int hash, KEY_TYPE key, VALUE_TYPE value) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) { insert(-slot-1, key, value); return getDefaultReturnValue(); } VALUE_TYPE oldValue = values[slot]; values[slot] = value; return oldValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE putIfAbsent(int hash, KEY_TYPE key, VALUE_TYPE value) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) { insert(-slot-1, key, value); return getDefaultReturnValue(); } else if(VALUE_EQUALS(values[slot], getDefaultReturnValue())) { VALUE_TYPE oldValue = values[slot]; values[slot] = value; return oldValue; } return values[slot]; } finally { unlockWrite(stamp); } } #if VALUE_PRIMITIVES protected VALUE_TYPE addTo(int hash, KEY_TYPE key, VALUE_TYPE value) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) { insert(-slot-1, key, value); return getDefaultReturnValue(); } VALUE_TYPE oldValue = values[slot]; values[slot] += value; return oldValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE subFrom(int hash, KEY_TYPE key, VALUE_TYPE value) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) return getDefaultReturnValue(); VALUE_TYPE oldValue = values[slot]; values[slot] -= value; if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); return oldValue; } finally { unlockWrite(stamp); } } #endif #if !TYPE_OBJECT protected boolean containsKey(int hash, KEY_TYPE key) { long stamp = readLock(); try { return findIndex(hash, key) >= 0; } finally { unlockRead(stamp); } } #endif @Deprecated protected boolean containsKey(int hash, Object key) { long stamp = readLock(); try { return findIndex(hash, key) >= 0; } finally { unlockRead(stamp); } } #if !VALUE_OBJECT protected boolean containsValue(VALUE_TYPE value) { long stamp = readLock(); try { int index = firstIndex; while(index != -1) { if(VALUE_EQUALS(values[index], value)) return true; index = (int)links[index]; } return false; } finally { unlockRead(stamp); } } #endif @Deprecated protected boolean containsValue(Object value) { long stamp = readLock(); try { int index = firstIndex; while(index != -1) { #if VALUE_OBJECT if(VALUE_EQUALS(values[index], value)) return true; #else if((value == null && values[index] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[index], value)) return true; #endif index = (int)links[index]; } return false; } finally { unlockRead(stamp); } } #if !TYPE_OBJECT protected VALUE_TYPE get(int hash, KEY_TYPE key) { long stamp = readLock(); try { int slot = findIndex(hash, key); return slot < 0 ? getDefaultReturnValue() : values[slot]; } finally { unlockRead(stamp); } } #endif protected VALUE_TYPE get(int hash, Object key) { long stamp = readLock(); try { int slot = findIndex(hash, key); return slot < 0 ? getDefaultReturnValue() : values[slot]; } finally { unlockRead(stamp); } } #if TYPE_OBJECT && VALUE_OBJECT protected VALUE_TYPE getOrDefault(int hash, Object key, VALUE_TYPE defaultValue) { long stamp = readLock(); try { int slot = findIndex(hash, key); return slot < 0 ? defaultValue : values[slot]; } finally { unlockRead(stamp); } } #else protected VALUE_TYPE getOrDefault(int hash, KEY_TYPE key, VALUE_TYPE defaultValue) { long stamp = readLock(); try { int slot = findIndex(hash, key); return slot < 0 ? defaultValue : values[slot]; } finally { unlockRead(stamp); } } #endif protected void forEach(BI_CONSUMER KEY_VALUE_GENERIC_TYPE action) { long stamp = readLock(); try { int index = firstIndex; while(index != -1) { action.accept(keys[index], values[index]); index = (int)links[index]; } } finally { unlockRead(stamp); } } #if !TYPE_OBJECT protected VALUE_TYPE remove(int hash, KEY_TYPE key) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) return getDefaultReturnValue(); return removeIndex(slot); } finally { unlockWrite(stamp); } } #endif protected VALUE_TYPE removeOrDefault(int hash, KEY_TYPE key, VALUE_TYPE defaultValue) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) return defaultValue; return removeIndex(slot); } finally { unlockWrite(stamp); } } protected CLASS_VALUE_TYPE remove(int hash, Object key) { long stamp = writeLock(); try { int slot = findIndex(hash, key); if(slot < 0) return VALUE_TO_OBJ(getDefaultReturnValue()); return VALUE_TO_OBJ(removeIndex(slot)); } finally { unlockWrite(stamp); } } #if !TYPE_OBJECT || !VALUE_OBJECT protected boolean remove(int hash, KEY_TYPE key, VALUE_TYPE value) { long stamp = writeLock(); try { if(KEY_EQUALS_NULL(key)) { if(containsNull && VALUE_EQUALS(value, values[nullIndex])) { removeNullIndex(); return true; } return false; } int pos = hash & mask; KEY_TYPE current = keys[pos]; if(KEY_EQUALS_NULL(current)) return false; if(KEY_EQUALS(current, key) && VALUE_EQUALS(value, values[pos])) { removeIndex(pos); return true; } while(true) { if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; else if(KEY_EQUALS(current, key) && VALUE_EQUALS(value, values[pos])) { removeIndex(pos); return true; } } } finally { unlockWrite(stamp); } } #endif protected boolean remove(int hash, Object key, Object value) { long stamp = writeLock(); try { #if TYPE_OBJECT if(key == null) { #else if(key == null || (key instanceof CLASS_TYPE && KEY_EQUALS_NULL(CLASS_TO_KEY(key)))) { #endif if(containsNull && EQUALS_VALUE_TYPE(values[nullIndex], value)) { removeNullIndex(); return true; } return false; } int pos = hash & mask; KEY_TYPE current = keys[pos]; if(KEY_EQUALS_NULL(current)) return false; if(EQUALS_KEY_TYPE(current, key) && EQUALS_VALUE_TYPE(values[pos], value)) { removeIndex(pos); return true; } while(true) { if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; else if(EQUALS_KEY_TYPE(current, key) && EQUALS_VALUE_TYPE(values[pos], value)){ removeIndex(pos); return true; } } } finally { unlockWrite(stamp); } } protected boolean replace(int hash, KEY_TYPE key, VALUE_TYPE oldValue, VALUE_TYPE newValue) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0 || values[index] != oldValue) return false; values[index] = newValue; return true; } finally { unlockWrite(stamp); } } protected VALUE_TYPE replace(int hash, KEY_TYPE key, VALUE_TYPE value) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) return getDefaultReturnValue(); VALUE_TYPE oldValue = values[index]; values[index] = value; return oldValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE compute(int hash, KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); #if VALUE_OBJECT if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; #endif insert(-index-1, key, newValue); return newValue; } VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); #if VALUE_OBJECT if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeIndex(index); return newValue; } #endif values[index] = newValue; return newValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE computeIfAbsent(int hash, KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.APPLY(key); #if VALUE_OBJECT if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; #endif insert(-index-1, key, newValue); return newValue; } VALUE_TYPE newValue = values[index]; #if VALUE_OBJECT if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { newValue = mappingFunction.APPLY(key); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; values[index] = newValue; } #endif return newValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE supplyIfAbsent(int hash, KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) { VALUE_TYPE newValue = valueProvider.VALUE_SUPPLY_GET(); #if VALUE_OBJECT if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; #endif insert(-index-1, key, newValue); return newValue; } VALUE_TYPE newValue = values[index]; #if VALUE_OBJECT if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { newValue = valueProvider.VALUE_SUPPLY_GET(); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; values[index] = newValue; } #endif return newValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE computeIfPresent(int hash, KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); #if !VALUE_OBJECT if(index < 0) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); #else if(index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeIndex(index); return newValue; } #endif values[index] = newValue; return newValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE computeNonDefault(int hash, KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; insert(-index-1, key, newValue); return newValue; } VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeIndex(index); return newValue; } values[index] = newValue; return newValue; } finally { unlockWrite(stamp); } } #if !VALUE_OBJECT protected VALUE_TYPE computeIfAbsentNonDefault(int hash, KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) { VALUE_TYPE newValue = mappingFunction.APPLY(key); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; insert(-index-1, key, newValue); return newValue; } VALUE_TYPE newValue = values[index]; if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { newValue = mappingFunction.APPLY(key); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; values[index] = newValue; } return newValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE supplyIfAbsentNonDefault(int hash, KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0) { VALUE_TYPE newValue = valueProvider.VALUE_SUPPLY_GET(); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; insert(-index-1, key, newValue); return newValue; } VALUE_TYPE newValue = values[index]; if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { newValue = valueProvider.VALUE_SUPPLY_GET(); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; values[index] = newValue; } return newValue; } finally { unlockWrite(stamp); } } protected VALUE_TYPE computeIfPresentNonDefault(int hash, KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); if(index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeIndex(index); return newValue; } values[index] = newValue; return newValue; } finally { unlockWrite(stamp); } } #endif protected VALUE_TYPE merge(int hash, KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { long stamp = writeLock(); try { int index = findIndex(hash, key); VALUE_TYPE newValue = index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(values[index], value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(index >= 0) removeIndex(index); } else if(index < 0) insert(-index-1, key, newValue); else values[index] = newValue; return newValue; } finally { unlockWrite(stamp); } } protected void clear() { if(size == 0) return; long stamp = writeLock(); try { size = 0; containsNull = false; Arrays.fill(keys, EMPTY_KEY_VALUE); Arrays.fill(values, EMPTY_VALUE); firstIndex = -1; lastIndex = -1; } finally { unlockWrite(stamp); } } protected boolean trim(int size) { int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); if(request >= mask+1 || this.size > Math.min((int)Math.ceil(request * loadFactor), request - 1)) return false; long stamp = writeLock(); try { try { rehash(request); } catch(OutOfMemoryError noMemory) { return false; } return true; } finally { unlockWrite(stamp); } } protected void clearAndTrim(int size) { int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); if(request >= mask+1) { clear(); return; } long stamp = writeLock(); try { if(nullIndex != -1) { nullIndex = request; } mask = request-1; maxFill = Math.min((int)Math.ceil(request * loadFactor), request - 1); int arraySize = request + (nullIndex != -1 ? 1 : 0); keys = NEW_KEY_ARRAY(arraySize); values = NEW_VALUE_ARRAY(arraySize); links = new long[arraySize]; this.size = 0; firstIndex = -1; lastIndex = -1; containsNull = false; } finally { unlockWrite(stamp); } } protected void insert(int slot, KEY_TYPE key, VALUE_TYPE value) { if(slot == nullIndex) containsNull = true; keys[slot] = key; values[slot] = value; if(size == 0) { firstIndex = lastIndex = slot; links[slot] = -1L; } else { links[lastIndex] ^= ((links[lastIndex] ^ (slot & 0xFFFFFFFFL)) & 0xFFFFFFFFL); links[slot] = ((lastIndex & 0xFFFFFFFFL) << 32) | 0xFFFFFFFFL; lastIndex = slot; } if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); } protected VALUE_TYPE removeIndex(int pos) { if(pos == nullIndex) return containsNull ? removeNullIndex() : getDefaultReturnValue(); VALUE_TYPE value = values[pos]; keys[pos] = EMPTY_KEY_VALUE; values[pos] = EMPTY_VALUE; size--; onNodeRemoved(pos); shiftKeys(pos); if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); return value; } protected VALUE_TYPE removeNullIndex() { VALUE_TYPE value = values[nullIndex]; containsNull = false; keys[nullIndex] = EMPTY_KEY_VALUE; values[nullIndex] = EMPTY_VALUE; size--; onNodeRemoved(nullIndex); if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); return value; } #if !TYPE_OBJECT protected int findIndex(int hash, KEY_TYPE key) { if(KEY_EQUALS_NULL(key)) return containsNull ? nullIndex : -(nullIndex + 1); int pos = hash & mask; KEY_TYPE current = keys[pos]; if(KEY_EQUALS_NOT_NULL(current)) { if(KEY_EQUALS(current, key)) return pos; while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) if(KEY_EQUALS(current, key)) return pos; } return -(pos + 1); } #endif protected int findIndex(int hash, Object key) { if(key == null) return containsNull ? nullIndex : -(nullIndex + 1); #if !TYPE_OBJECT if(KEY_EQUALS_NULL(CLASS_TO_KEY(key))) return containsNull ? nullIndex : -(nullIndex + 1); #endif int pos = hash & mask; KEY_TYPE current = keys[pos]; if(KEY_EQUALS_NOT_NULL(current)) { if(EQUALS_KEY_TYPE(current, key)) return pos; while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) if(EQUALS_KEY_TYPE(current, key)) return pos; } return -(pos + 1); } protected void shiftKeys(int startPos) { int slot, last; KEY_TYPE current; while(true) { startPos = ((last = startPos) + 1) & mask; while(true){ if(KEY_EQUALS_NULL((current = keys[startPos]))) { keys[last] = EMPTY_KEY_VALUE; values[last] = EMPTY_VALUE; return; } slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; startPos = ++startPos & mask; } keys[last] = current; values[last] = values[startPos]; onNodeMoved(startPos, last); } } protected void rehash(int newSize) { int newMask = newSize - 1; int arraySize = newSize + (nullIndex != -1 ? 1 : 0); KEY_TYPE[] newKeys = NEW_KEY_ARRAY(arraySize); VALUE_TYPE[] newValues = NEW_VALUE_ARRAY(arraySize); long[] newLinks = new long[arraySize]; int i = firstIndex, prev = -1, newPrev = -1, pos; firstIndex = -1; for(int j = size; j-- != 0;) { if(KEY_EQUALS_NULL(keys[i])) pos = newSize; else { pos = HashUtil.mix(KEY_TO_HASH(keys[i])) & newMask; while(KEY_EQUALS_NOT_NULL(newKeys[pos])) pos = ++pos & newMask; } newKeys[pos] = keys[i]; newValues[pos] = values[i]; if(prev != -1) { newLinks[newPrev] ^= ((newLinks[newPrev] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); newLinks[pos] ^= ((newLinks[pos] ^ ((newPrev & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); newPrev = pos; } else { newPrev = firstIndex = pos; newLinks[pos] = -1L; } i = (int)links[prev = i]; } links = newLinks; lastIndex = newPrev; if(newPrev != -1) newLinks[newPrev] |= 0xFFFFFFFFL; if(nullIndex != -1) { nullIndex = newSize; } mask = newMask; maxFill = Math.min((int)Math.ceil(newSize * loadFactor), newSize - 1); keys = newKeys; values = newValues; } protected void onNodeRemoved(int pos) { if(size == 0) firstIndex = lastIndex = -1; else if(firstIndex == pos) { firstIndex = (int)links[pos]; if(0 <= firstIndex) links[firstIndex] |= 0xFFFFFFFF00000000L; } else if(lastIndex == pos) { lastIndex = (int)(links[pos] >>> 32); if(0 <= lastIndex) links[lastIndex] |= 0xFFFFFFFFL; } else { long link = links[pos]; int prev = (int)(link >>> 32); int next = (int)link; links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); } } protected void onNodeMoved(int from, int to) { if(size == 1) { firstIndex = lastIndex = to; links[to] = -1L; } else if(firstIndex == from) { firstIndex = to; links[(int)links[from]] ^= ((links[(int)links[from]] ^ ((to & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); links[to] = links[from]; } else if(lastIndex == from) { lastIndex = to; links[(int)(links[from] >>> 32)] ^= ((links[(int)(links[from] >>> 32)] ^ (to & 0xFFFFFFFFL)) & 0xFFFFFFFFL); links[to] = links[from]; } else { long link = links[from]; int prev = (int)(link >>> 32); int next = (int)link; links[prev] ^= ((links[prev] ^ (to & 0xFFFFFFFFL)) & 0xFFFFFFFFL); links[next] ^= ((links[next] ^ ((to & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); links[to] = link; } } } }