package speiger.src.collections.PACKAGE.maps.impl.hash; import java.util.Arrays; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.function.Consumer; #if !TYPE_OBJECT import speiger.src.collections.PACKAGE.collections.ITERATOR; import speiger.src.collections.PACKAGE.functions.CONSUMER; #endif import speiger.src.collections.PACKAGE.functions.consumer.BI_CONSUMER; import speiger.src.collections.PACKAGE.lists.ARRAY_LIST; import speiger.src.collections.PACKAGE.lists.LIST; import speiger.src.collections.PACKAGE.maps.abstracts.ABSTRACT_MAP; import speiger.src.collections.PACKAGE.maps.interfaces.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; #if !SAME_TYPE && !VALUE_OBJECT import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_ITERATOR; import speiger.src.collections.VALUE_PACKAGE.functions.VALUE_CONSUMER; #endif import speiger.src.collections.objects.collections.ObjectIterator; import speiger.src.collections.objects.sets.AbstractObjectSet; import speiger.src.collections.objects.sets.ObjectSet; import speiger.src.collections.utils.HashUtil; public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENERIC_TYPE { protected transient KEY_TYPE[] keys; protected transient VALUE_TYPE[] values; protected transient boolean containsNull; protected transient int minCapacity; protected transient int nullIndex; protected transient int maxFill; protected transient int mask; protected transient FastEntrySet KEY_VALUE_GENERIC_TYPE entrySet; protected transient SET KEY_GENERIC_TYPE keySet; protected transient VALUE_COLLECTION VALUE_GENERIC_TYPE valuesC; protected int size; protected final float loadFactor; public HASH_MAP() { this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); } public HASH_MAP(int minCapacity) { this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); } public HASH_MAP(int minCapacity, float loadFactor) { 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"); this.loadFactor = loadFactor; this.minCapacity = nullIndex = HashUtil.arraySize(minCapacity, loadFactor); mask = nullIndex - 1; maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); keys = NEW_KEY_ARRAY(nullIndex + 1); values = NEW_VALUE_ARRAY(nullIndex + 1); } #if !TYPE_OBJECT || !VALUE_OBJECT public HASH_MAP(CLASS_TYPE[] keys, CLASS_VALUE_TYPE[] values) { this(keys, values, HashUtil.DEFAULT_LOAD_FACTOR); } public HASH_MAP(CLASS_TYPE[] keys, CLASS_VALUE_TYPE[] values, float loadFactor) { this(keys.length, loadFactor); if(keys.length != values.length) throw new IllegalStateException("Input Arrays are not equal size"); for(int i = 0,m=keys.length;i map) { this(map, HashUtil.DEFAULT_LOAD_FACTOR); } public HASH_MAP(Map map, float loadFactor) { this(map.size(), loadFactor); putAll(map); } public HASH_MAP(MAP KEY_VALUE_GENERIC_TYPE map) { this(map, HashUtil.DEFAULT_LOAD_FACTOR); } public HASH_MAP(MAP KEY_VALUE_GENERIC_TYPE map, float loadFactor) { this(map.size(), loadFactor); putAll(map); } @Override public VALUE_TYPE put(KEY_TYPE key, VALUE_TYPE value) { int slot = findIndex(key); if(slot < 0) { insert(-slot-1, key, value); return getDefaultReturnValue(); } VALUE_TYPE oldValue = values[slot]; values[slot] = value; return oldValue; } @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { int slot = findIndex(key); if(slot < 0) { insert(-slot-1, key, value); return getDefaultReturnValue(); } return values[slot]; } #if VALUE_PRIMITIVES @Override public VALUE_TYPE addTo(KEY_TYPE key, VALUE_TYPE value) { int slot = findIndex(key); if(slot < 0) { insert(-slot-1, key, value); return getDefaultReturnValue(); } VALUE_TYPE oldValue = values[slot]; values[slot] += value; return oldValue; } #endif #if !TYPE_OBJECT @Override public boolean containsKey(KEY_TYPE key) { return findIndex(key) >= 0; } #endif @Override @Deprecated public boolean containsKey(Object key) { return findIndex(key) >= 0; } #if !VALUE_OBJECT @Override public boolean containsValue(VALUE_TYPE value) { if(VALUE_EQUALS(value, values[nullIndex])) return true; for(int i = nullIndex-1;i >= 0;i--) if(KEY_EQUALS_NOT_NULL(keys[i]) && VALUE_EQUALS(values[i], value)) return true; return false; } #endif @Override @Deprecated public boolean containsValue(Object value) { if((value == null && VALUE_EQUALS(values[nullIndex], getDefaultReturnValue())) || EQUALS_VALUE_TYPE(values[nullIndex], value)) return true; for(int i = nullIndex-1;i >= 0;i--) if(KEY_EQUALS_NOT_NULL(keys[i]) && ((value == null && values[i] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[i], value))) return true; return false; } @Override public VALUE_TYPE REMOVE_KEY(KEY_TYPE key) { int slot = findIndex(key); if(slot < 0) return getDefaultReturnValue(); return removeIndex(slot); } @Override @Deprecated public CLASS_VALUE_TYPE remove(Object key) { int slot = findIndex(key); if(slot < 0) return VALUE_TO_OBJ(getDefaultReturnValue()); return removeIndex(slot); } #if !TYPE_OBJECT || !VALUE_OBJECT @Override public boolean remove(KEY_TYPE key, VALUE_TYPE value) { if(KEY_EQUALS_NULL(key)) { if(containsNull && VALUE_EQUALS(value, values[nullIndex])) { removeNullIndex(); return true; } return false; } int pos = HashUtil.mix(KEY_TO_HASH(key)) & 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; } } } #endif @Override public boolean remove(Object key, Object value) { Objects.requireNonNull(value); if(key == null) { if(containsNull && EQUALS_VALUE_TYPE(values[nullIndex], value)) { removeNullIndex(); return true; } return false; } int pos = HashUtil.mix(key.hashCode()) & 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; } } } @Override public VALUE_TYPE GET_VALUE(KEY_TYPE key) { int slot = findIndex(key); return slot < 0 ? getDefaultReturnValue() : values[slot]; } @Override public CLASS_VALUE_TYPE get(Object key) { int slot = findIndex(key); return VALUE_TO_OBJ(slot < 0 ? getDefaultReturnValue() : values[slot]); } #if TYPE_OBJECT && VALUE_OBJECT @Override public VALUE_TYPE getOrDefault(Object key, VALUE_TYPE defaultValue) { int slot = findIndex(key); return slot < 0 ? defaultValue : values[slot]; } #else @Override public VALUE_TYPE getOrDefault(KEY_TYPE key, VALUE_TYPE defaultValue) { int slot = findIndex(key); return slot < 0 ? defaultValue : values[slot]; } #endif @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(valuesC == null) valuesC = new Values(); return valuesC; } @Override public void forEach(BI_CONSUMER KEY_VALUE_GENERIC_TYPE action) { if(size() <= 0) return; if(containsNull) action.accept(keys[nullIndex], values[nullIndex]); for(int i = nullIndex-1;i>=0;i--) { if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i], values[i]); } } @Override public int size() { return size; } @Override public void clear() { if(size == 0) return; size = 0; containsNull = false; Arrays.fill(keys, EMPTY_KEY_VALUE); Arrays.fill(values, EMPTY_VALUE); } #if !TYPE_OBJECT protected int findIndex(KEY_TYPE key) { if(KEY_EQUALS_NULL(key)) return containsNull ? nullIndex : -(nullIndex + 1); int pos = HashUtil.mix(KEY_TO_HASH(key)) & 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(Object key) { if(key == null) return containsNull ? nullIndex : -(nullIndex + 1); int pos = HashUtil.mix(key.hashCode()) & 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 VALUE_TYPE removeIndex(int pos) { VALUE_TYPE value = values[pos]; 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; } protected void insert(int slot, KEY_TYPE key, VALUE_TYPE value) { if(slot == nullIndex) containsNull = true; keys[slot] = key; values[slot] = value; onNodeAdded(slot); if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); } protected void rehash(int newSize) { int newMask = newSize - 1; KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); VALUE_TYPE[] newValues = NEW_VALUE_ARRAY(newSize + 1); for(int i = nullIndex, pos = 0, j = (size - (containsNull ? 1 : 0));j-- != 0;) { while(KEY_EQUALS_NULL(keys[--i])); if(KEY_EQUALS_NOT_NULL(newKeys[pos = HashUtil.mix(KEY_TO_HASH(keys[i])) & newMask])) while(KEY_EQUALS_NOT_NULL(newKeys[pos = (++pos & newMask)])); newKeys[pos] = keys[i]; newValues[pos] = values[i]; } newValues[newSize] = values[nullIndex]; nullIndex = newSize; mask = newMask; maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); keys = newKeys; values = newValues; } protected void onNodeAdded(int pos) { } protected void onNodeRemoved(int pos) { } protected void onNodeMoved(int from, int to) { } 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 class MapEntry implements MAP.Entry KEY_VALUE_GENERIC_TYPE, Map.Entry { public int index = -1; public MapEntry() {} public MapEntry(int index) { this.index = index; } @Override public KEY_TYPE ENTRY_KEY() { return keys[index]; } @Override public VALUE_TYPE ENTRY_VALUE() { return values[index]; } @Override public VALUE_TYPE setValue(VALUE_TYPE value) { VALUE_TYPE oldValue = values[index]; values[index] = value; return oldValue; } @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(keys[index], entry.ENTRY_KEY()) && VALUE_EQUALS(values[index], 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(keys[index], key) && VALUE_EQUALS(values[index], value); #else if TYPE_OBJECT return value instanceof CLASS_VALUE_TYPE && KEY_EQUALS(keys[index], key) && VALUE_EQUALS(values[index], CLASS_TO_VALUE(value)); #else if VALUE_OBJECT return key instanceof CLASS_TYPE && KEY_EQUALS(keys[index], CLASS_TO_KEY(key)) && VALUE_EQUALS(values[index], value); #else return key instanceof CLASS_TYPE && value instanceof CLASS_VALUE_TYPE && KEY_EQUALS(keys[index], CLASS_TO_KEY(key)) && VALUE_EQUALS(values[index], CLASS_TO_VALUE(value)); #endif } return false; } @Override public int hashCode() { return KEY_TO_HASH(keys[index]) ^ VALUE_TO_HASH(values[index]); } @Override public String toString() { return KEY_TO_STRING(keys[index]) + "->" + VALUE_TO_STRING(values[index]); } } private final class MapEntrySet extends AbstractObjectSet implements MAP.FastEntrySet KEY_VALUE_GENERIC_TYPE { @Override public ObjectIterator fastIterator() { return new FastEntryIterator(); } @Override public ObjectIterator iterator() { return new EntryIterator(); } @Override public void forEach(Consumer action) { if(containsNull) action.accept(new BasicEntryKV_BRACES(keys[nullIndex], values[nullIndex])); for(int i = nullIndex-1;i>=0;i--) if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(new BasicEntryKV_BRACES(keys[i], values[i])); } @Override public void fastForEach(Consumer action) { BasicEntry KEY_VALUE_GENERIC_TYPE entry = new BasicEntryKV_BRACES(); if(containsNull) { entry.set(keys[nullIndex], values[nullIndex]); action.accept(entry); } for(int i = nullIndex-1;i>=0;i--) { if(KEY_EQUALS_NOT_NULL(keys[i])) { entry.set(keys[i], values[i]); action.accept(entry); } } } @Override public int size() { return HASH_MAP.this.size(); } @Override public void clear() { HASH_MAP.this.clear(); } @Override public boolean contains(Object o) { if(o instanceof Map.Entry) { if(o instanceof MAP.Entry) return HASH_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); return HASH_MAP.this.containsKey(((Map.Entry)o).getKey()); } return false; } @Override 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 HASH_MAP.this.remove(entry.ENTRY_KEY(), entry.ENTRY_VALUE()); } Map.Entry entry = (Map.Entry)o; return HASH_MAP.this.remove(entry.getKey(), entry.getValue()); } return false; } } private final class KeySet extends ABSTRACT_SET KEY_GENERIC_TYPE { #if TYPE_OBJECT @Override public boolean contains(Object e) { return containsKey(e); } @Override public boolean remove(Object o) { int oldSize = size; 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; remove(o); return size != oldSize; } #endif @Override public boolean add(KEY_TYPE o) { throw new UnsupportedOperationException(); } @Override public ITERATOR KEY_GENERIC_TYPE iterator() { return new KeyIterator(); } @Override public int size() { return HASH_MAP.this.size(); } @Override public void clear() { HASH_MAP.this.clear(); } #if TYPE_OBJECT @Override public void forEach(Consumer KEY_SUPER_GENERIC_TYPE action) { if(containsNull) action.accept(keys[nullIndex]); for(int i = nullIndex-1;i>=0;i--) if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i]); } #else @Override public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { if(containsNull) action.accept(keys[nullIndex]); for(int i = nullIndex-1;i>=0;i--) if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i]); } #endif } private class Values extends VALUE_ABSTRACT_COLLECTION VALUE_GENERIC_TYPE { #if VALUE_OBJECT @Override public boolean contains(Object e) { return containsValue(e); } #else @Override public boolean contains(VALUE_TYPE e) { return containsValue(e); } #endif @Override public boolean add(VALUE_TYPE o) { throw new UnsupportedOperationException(); } @Override public VALUE_ITERATOR VALUE_GENERIC_TYPE iterator() { return new ValueIterator(); } @Override public int size() { return HASH_MAP.this.size(); } @Override public void clear() { HASH_MAP.this.clear(); } #if VALUE_OBJECT @Override public void forEach(Consumer VALUE_SUPER_GENERIC_TYPE action) { if(containsNull) action.accept(values[nullIndex]); for(int i = nullIndex-1;i>=0;i--) if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(values[i]); } #else @Override public void forEach(VALUE_CONSUMER VALUE_SUPER_GENERIC_TYPE action) { if(containsNull) action.accept(values[nullIndex]); for(int i = nullIndex-1;i>=0;i--) if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(values[i]); } #endif } private class FastEntryIterator extends MapIterator implements ObjectIterator { MapEntry entry = new MapEntry(); @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { entry.index = nextEntry(); return entry; } } private class EntryIterator extends MapIterator implements ObjectIterator { MapEntry entry; @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { return entry = new MapEntry(nextEntry()); } @Override public void remove() { super.remove(); entry.index = -1; } } private class KeyIterator extends MapIterator implements ITERATOR KEY_GENERIC_TYPE { @Override public KEY_TYPE NEXT() { return keys[nextEntry()]; } } private class ValueIterator extends MapIterator implements VALUE_ITERATOR VALUE_GENERIC_TYPE { @Override public VALUE_TYPE VALUE_NEXT() { return values[nextEntry()]; } } private class MapIterator { int pos = nullIndex; int lastReturned = -1; int nextIndex = Integer.MIN_VALUE; boolean returnNull = containsNull; LIST KEY_GENERIC_TYPE wrapped = null; public boolean hasNext() { if(nextIndex == Integer.MIN_VALUE) { if(returnNull) { returnNull = false; nextIndex = nullIndex; } else { while(true) { if(--pos < 0) { if(wrapped == null || wrapped.size() <= -pos - 1) break; nextIndex = -pos - 1; break; } if(KEY_EQUALS_NOT_NULL(keys[pos])){ nextIndex = pos; break; } } } } return nextIndex != Integer.MIN_VALUE; } public int nextEntry() { if(!hasNext()) throw new NoSuchElementException(); if(nextIndex < 0){ lastReturned = Integer.MAX_VALUE; int value = findIndex(wrapped.GET_KEY(nextIndex)); if(value < 0) throw new IllegalStateException("Entry ["+nextIndex+"] was removed during Iteration"); nextIndex = Integer.MIN_VALUE; return value; } int value = (lastReturned = nextIndex); nextIndex = Integer.MIN_VALUE; return value; } public void remove() { if(lastReturned == -1) throw new IllegalStateException(); if(lastReturned == nullIndex) { containsNull = false; keys[nullIndex] = EMPTY_KEY_VALUE; values[nullIndex] = EMPTY_VALUE; } else if(pos >= 0) shiftKeys(pos); else { HASH_MAP.this.remove(wrapped.GET_KEY(-pos - 1)); return; } size--; lastReturned = -1; } private 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; } if(startPos < last) { if(wrapped == null) wrapped = new ARRAY_LISTBRACES(2); wrapped.add(keys[startPos]); } keys[last] = current; values[last] = values[startPos]; } } } }