diff --git a/build.gradle b/build.gradle index c1f0242..739a30e 100644 --- a/build.gradle +++ b/build.gradle @@ -56,7 +56,7 @@ dependencies { builderImplementation 'com.google.code.gson:gson:2.10' builderImplementation 'de.speiger:Simple-Code-Generator:1.3.0' testImplementation 'junit:junit:4.12' - testImplementation 'com.google.guava:guava-testlib:31.0.1-jre' + testImplementation 'com.google.guava:guava-testlib:33.6.0-jre' } diff --git a/src/builder/java/speiger/src/builder/modules/MapModule.java b/src/builder/java/speiger/src/builder/modules/MapModule.java index 87ce295..206d5d6 100644 --- a/src/builder/java/speiger/src/builder/modules/MapModule.java +++ b/src/builder/java/speiger/src/builder/modules/MapModule.java @@ -36,6 +36,10 @@ public class MapModule extends BaseModule public static final FunctionDependency ENUM_MAP = MODULE.createDependency("EnumMap").addEntryDependency(IMPLEMENTATION); public static final FunctionDependency LINKED_ENUM_MAP = MODULE.createDependency("LinkedEnumMap").addEntryDependency(ENUM_MAP).addEntryDependency(ORDERED_MAP); + public static final FunctionDependency REF_MAP = MODULE.createDependency("ReferenceHashMap").addEntryDependency(IMPLEMENTATION); + public static final FunctionDependency LINKED_REF_MAP = MODULE.createDependency("LinkedReferenceMap").addEntryDependency(REF_MAP).addEntryDependency(ORDERED_MAP); + + public static final FunctionDependency CONCURRENT_MAP = MODULE.createDependency("ConcurrentMap").addEntryDependency(IMPLEMENTATION); public static final FunctionDependency AVL_TREE_MAP = MODULE.createDependency("AVLTreeMap").addEntryDependency(SORTED_MAP).addEntryDependency(IMPLEMENTATION); public static final FunctionDependency RB_TREE_MAP = MODULE.createDependency("RBTreeMap").addEntryDependency(SORTED_MAP).addEntryDependency(IMPLEMENTATION); @@ -51,7 +55,7 @@ public class MapModule extends BaseModule @Override public List getDependencies(ClassType keyType, ClassType valueType) { List dependencies = new ArrayList<>(Arrays.asList(MODULE, ORDERED_MAP, SORTED_MAP, IMPLEMENTATION, WRAPPERS, ARRAY_MAP, IMMUTABLE_MAP, HASH_MAP, LINKED_MAP, CUSTOM_MAP, LINKED_CUSTOM_MAP, CONCURRENT_MAP, AVL_TREE_MAP, RB_TREE_MAP)); - if(keyType == ClassType.OBJECT) dependencies.addAll(Arrays.asList(ENUM_MAP, LINKED_ENUM_MAP)); + if(keyType == ClassType.OBJECT) dependencies.addAll(Arrays.asList(ENUM_MAP, LINKED_ENUM_MAP, REF_MAP, LINKED_REF_MAP)); return dependencies; } @@ -70,6 +74,10 @@ public class MapModule extends BaseModule if(AVL_TREE_MAP.isEnabled()) addFlag("AVL_TREE_MAP_FEATURE"); if(RB_TREE_MAP.isEnabled()) addFlag("RB_TREE_MAP_FEATURE"); + if(REF_MAP.isEnabled()) addFlag("REF_MAP_FEATURE"); + if(LINKED_REF_MAP.isEnabled()) addFlag("LINKED_REF_MAP_FEATURE"); + + if(CONCURRENT_MAP.isEnabled()) addFlag("CONCURRENT_MAP_FEATURE"); if(IMMUTABLE_MAP.isEnabled()) addFlag("IMMUTABLE_MAP_FEATURE"); if(HASH_MAP.isEnabled()) addFlag("MAP_FEATURE"); @@ -85,6 +93,8 @@ public class MapModule extends BaseModule if(!IMMUTABLE_MAP.isEnabled()) addBlockedFiles("ImmutableOpenHashMap"); if(!CONCURRENT_MAP.isEnabled()) addBlockedFiles("ConcurrentMap", "ConcurrentOpenHashMap"); if(!ORDERED_MAP.isEnabled()) addBlockedFiles("OrderedMap"); + if(!REF_MAP.isEnabled()) addBlockedFiles("ReferenceHashMap"); + if(!LINKED_REF_MAP.isEnabled()) addBlockedFiles("LinkedReferenceHashMap"); if(!HASH_MAP.isEnabled()) addBlockedFiles("OpenHashMap"); if(!LINKED_MAP.isEnabled()) addBlockedFiles("LinkedOpenHashMap"); if(!CUSTOM_MAP.isEnabled()) addBlockedFiles("OpenCustomHashMap"); @@ -127,6 +137,8 @@ public class MapModule extends BaseModule addBiRequirement("AbstractMap"); addEnumRequirement("EnumMap"); addEnumRequirement("LinkedEnumMap"); + addEnumRequirement("ReferenceHashMap"); + addEnumRequirement("LinkedReferenceHashMap"); addBiRequirement("ConcurrentOpenHashMap"); addBiRequirement("ImmutableOpenHashMap"); addBiRequirement("OpenHashMap"); @@ -141,6 +153,8 @@ public class MapModule extends BaseModule addRemapper("AbstractMap", "Abstract%sMap"); addRemapper("EnumMap", "Enum2%sMap"); addRemapper("LinkedEnumMap", "LinkedEnum2%sMap"); + addRemapper("ReferenceHashMap", "Reference2%sHashMap"); + addRemapper("LinkedReferenceHashMap", "Reference2%sLinkedHashMap"); addRemapper("ImmutableOpenHashMap", "Immutable%sOpenHashMap"); //Test Classes @@ -249,6 +263,8 @@ public class MapModule extends BaseModule addBiClassMapper("RB_TREE_MAP", "RBTreeMap", "2"); addFunctionValueMappers("LINKED_ENUM_MAP", valueType.isObject() ? "LinkedEnum2ObjectMap" : "LinkedEnum2%sMap"); addFunctionValueMappers("ENUM_MAP", valueType.isObject() ? "Enum2ObjectMap" : "Enum2%sMap"); + addFunctionValueMappers("REF_MAP", valueType.isObject() ? "Reference2ObjectHashMap" : "Reference2%sHashMap"); + addFunctionValueMappers("LINKED_REF_MAP", valueType.isObject() ? "Reference2ObjectLinkedHashMap" : "Reference2%sLinkedHashMap"); addBiClassMapper("HASH_MAP", "OpenHashMap", "2"); addBiClassMapper("ARRAY_MAP", "ArrayMap", "2"); diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template index bf0681d..535f4ae 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/LinkedOpenHashMap.template @@ -288,7 +288,7 @@ public class LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE extends HASH_MAP KEY_VALUE_G } else { int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; - while(KEY_EQUALS_NULL(key)) { + while(KEY_EQUALS_NOT_NULL(keys[pos])) { if(KEY_EQUALS(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -312,7 +312,7 @@ public class LINKED_HASH_MAP KEY_VALUE_GENERIC_TYPE extends HASH_MAP KEY_VALUE_G } else { int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; - while(KEY_EQUALS_NULL(key)) { + while(KEY_EQUALS_NOT_NULL(keys[pos])) { if(KEY_EQUALS(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/LinkedReferenceHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/LinkedReferenceHashMap.template new file mode 100644 index 0000000..b618035 --- /dev/null +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/LinkedReferenceHashMap.template @@ -0,0 +1,1581 @@ +package speiger.src.collections.PACKAGE.maps.impl.reference; + +import java.util.Arrays; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.BiFunction; +import java.util.function.Predicate; +#if !TYPE_OBJECT && JDK_TYPE +import java.util.function.PREDICATE; +import java.util.OPTIONAL; +#endif +#if !SAME_TYPE && JDK_VALUE && !VALUE_OBJECT +import java.util.function.VALUE_PREDICATE; +import java.util.VALUE_OPTIONAL; +#endif + +import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; +#if !TYPE_OBJECT +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_OBJECT && !JDK_TYPE +import speiger.src.collections.PACKAGE.functions.OPTIONAL; +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +#if !TYPE_INT || !SAME_TYPE +import speiger.src.collections.PACKAGE.functions.consumer.BI_CONSUMER; +#endif +import speiger.src.collections.PACKAGE.functions.function.SINGLE_UNARY_OPERATOR; +import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; +import speiger.src.collections.PACKAGE.maps.interfaces.MAP; +import speiger.src.collections.PACKAGE.maps.interfaces.ORDERED_MAP; +import speiger.src.collections.PACKAGE.sets.ABSTRACT_SET; +import speiger.src.collections.PACKAGE.sets.ORDERED_SET; +import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_ABSTRACT_COLLECTION; +import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_ORDERED_COLLECTION; +import speiger.src.collections.VALUE_PACKAGE.collections.VALUE_ITERATOR; +#if !SAME_TYPE +import speiger.src.collections.VALUE_PACKAGE.functions.function.VALUE_UNARY_OPERATOR; +#endif +#if !VALUE_OBJECT && !SAME_TYPE +import speiger.src.collections.VALUE_PACKAGE.functions.VALUE_CONSUMER; +import speiger.src.collections.VALUE_PACKAGE.lists.VALUE_LIST_ITERATOR; +#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; +import speiger.src.collections.VALUE_PACKAGE.functions.VALUE_OPTIONAL; +#endif +#endif +#if !TYPE_OBJECT +#if !VALUE_OBJECT +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; + +#endif +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +#endif +import speiger.src.collections.utils.HashUtil; + +/** + * A Type Specific LinkedHashMap implementation that uses specific arrays to create links between nodes to remove the wrapping of elements + * to greatly reduce memory usage. In Addition adding some helper methods to move around elements. + * This implementation of SortedMap does not support SubMaps of any kind. It implements the interface due to sortability and first/last access + * @Type(T) + * @ValueType(V) + */ +public class LINKED_REF_MAP KEY_VALUE_GENERIC_TYPE extends REF_MAP KEY_VALUE_GENERIC_TYPE implements ORDERED_MAP KEY_VALUE_GENERIC_TYPE +{ + /** 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; + + /** + * Default Constructor + */ + public LINKED_REF_MAP() { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * 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 LINKED_REF_MAP(int minCapacity) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * 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 LINKED_REF_MAP(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + +#if !TYPE_OBJECT || !VALUE_OBJECT + /** + * Helper constructor that allow to create a map from boxed values (it will unbox them) + * @param keys the keys that should be put into the map + * @param values the values that should be put into the map. + * @throws IllegalStateException if the keys and values do not match in lenght + */ + public LINKED_REF_MAP(CLASS_TYPE[] keys, CLASS_VALUE_TYPE[] values) { + this(keys, values, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a map from boxed values (it will unbox them) + * @param keys the keys that should be put into the map + * @param values the values that should be put into the map. + * @param loadFactor the percentage of how full the backing array can be before they resize + * @throws IllegalStateException if the keys and values do not match in lenght + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public LINKED_REF_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); + } + + /** + * 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 LINKED_REF_MAP(Map map, float loadFactor) { + this(map.size(), loadFactor); + 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 LINKED_REF_MAP(MAP KEY_VALUE_GENERIC_TYPE map) { + this(map, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * 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 LINKED_REF_MAP(MAP KEY_VALUE_GENERIC_TYPE map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public VALUE_TYPE putAndMoveToFirst(KEY_TYPE key, VALUE_TYPE value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos].get(), key)) { + VALUE_TYPE lastValue = values[pos]; + values[pos] = value; + moveToFirstIndex(pos, false); + return lastValue; + } + pos = ++pos & mask; + } + keys[pos] = new WeakKey<>(key, queue).index(pos); + values[pos] = value; + onNodeAdded(pos); + moveToFirstIndex(pos, true); + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return getDefaultReturnValue(); + } + + @Override + public VALUE_TYPE putAndMoveToLast(KEY_TYPE key, VALUE_TYPE value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos].get(), key)) { + VALUE_TYPE lastValue = values[pos]; + values[pos] = value; + moveToLastIndex(pos, false); + return lastValue; + } + pos = ++pos & mask; + } + keys[pos] = new WeakKey<>(key, queue).index(pos); + values[pos] = value; + onNodeAdded(pos); + moveToLastIndex(pos, true); + + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return getDefaultReturnValue(); + } + + @Override + public VALUE_TYPE putFirst(KEY_TYPE key, VALUE_TYPE value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos].get(), key)) return values[pos]; + pos = ++pos & mask; + } + keys[pos] = new WeakKey<>(key, queue).index(pos); + values[pos] = value; + onNodeAdded(pos); + moveToFirstIndex(pos, true); + + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return getDefaultReturnValue(); + } + + @Override + public VALUE_TYPE putLast(KEY_TYPE key, VALUE_TYPE value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos].get(), key)) return values[pos]; + pos = ++pos & mask; + } + keys[pos] = new WeakKey<>(key, queue).index(pos); + values[pos] = value; + onNodeAdded(pos); + moveToLastIndex(pos, true); + + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return getDefaultReturnValue(); + } + + @Override + public boolean moveToFirst(KEY_TYPE key) { + Objects.requireNonNull(key); + if(isEmpty() || KEY_EQUALS(FIRST_ENTRY_KEY(), key)) return false; + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(KEY_TYPE key) { + Objects.requireNonNull(key); + if(isEmpty() || KEY_EQUALS(LAST_ENTRY_KEY(), key)) return false; + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public VALUE_TYPE getAndMoveToFirst(KEY_TYPE key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public VALUE_TYPE getAndMoveToLast(KEY_TYPE key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + +#if !VALUE_OBJECT + @Override + public boolean containsValue(VALUE_TYPE value) { + int index = firstIndex; + while(index != -1) { + if(VALUE_EQUALS(values[index], value)) return true; + index = (int)links[index]; + } + return false; + } + +#endif + @Override + @ValuePrimitive + public boolean containsValue(Object value) { + 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; + } + + @Override + public LINKED_REF_MAP KEY_VALUE_GENERIC_TYPE copy() { + LINKED_REF_MAP KEY_VALUE_GENERIC_TYPE map = new LINKED_REF_MAPKV_BRACES(0, loadFactor); + map.minCapacity = minCapacity; + map.mask = mask; + map.maxFill = maxFill; + map.capacity = capacity; + map.size = size; + map.keys = new WeakKey[keys.length]; + for(int i = 0,m=keys.length;i(keys[i].get(), map.queue).index(i); + } + } + map.values = Arrays.copyOf(values, values.length); + map.links = Arrays.copyOf(links, links.length); + map.firstIndex = firstIndex; + map.lastIndex = lastIndex; + return map; + } + + @Override + public KEY_TYPE FIRST_ENTRY_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public KEY_TYPE POLL_FIRST_ENTRY_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + WeakKey result = keys[pos]; + size--; + shiftKeys(pos); + if(capacity > minCapacity && size < maxFill / 4 && capacity > HashUtil.DEFAULT_MIN_CAPACITY) rehash(capacity / 2); + return result.get(); + } + + @Override + public KEY_TYPE LAST_ENTRY_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public KEY_TYPE POLL_LAST_ENTRY_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + WeakKey result = keys[pos]; + size--; + shiftKeys(pos); + if(capacity > minCapacity && size < maxFill / 4 && capacity > HashUtil.DEFAULT_MIN_CAPACITY) rehash(capacity / 2); + return result.get(); + } + + @Override + public VALUE_TYPE FIRST_ENTRY_VALUE() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public VALUE_TYPE LAST_ENTRY_VALUE() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntryKV_BRACES(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntryKV_BRACES(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry KEY_VALUE_GENERIC_TYPE result = new BasicEntryKV_BRACES(keys[pos].get(), values[pos]); + size--; + shiftKeys(pos); + if(capacity > minCapacity && size < maxFill / 4 && capacity > HashUtil.DEFAULT_MIN_CAPACITY) rehash(capacity / 2); + return result; + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry KEY_VALUE_GENERIC_TYPE result = new BasicEntryKV_BRACES(keys[pos].get(), values[pos]); + size--; + shiftKeys(pos); + if(capacity > minCapacity && size < maxFill / 4 && capacity > HashUtil.DEFAULT_MIN_CAPACITY) rehash(capacity / 2); + return result; + } + + @Override + public ObjectOrderedSet ENTRY_SET() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet)entrySet; + } + + @Override + public ORDERED_SET KEY_GENERIC_TYPE keySet() { + if(keySet == null) keySet = new KeySet(); + return (ORDERED_SET KEY_GENERIC_TYPE)keySet; + } + + @Override + public VALUE_ORDERED_COLLECTION VALUE_GENERIC_TYPE values() { + if(valuesC == null) valuesC = new Values(); + return (VALUE_ORDERED_COLLECTION VALUE_GENERIC_TYPE)valuesC; + } + + @Override + public void forEach(BI_CONSUMER KEY_VALUE_GENERIC_TYPE action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get(), values[index]); + index = (int)links[index]; + } + } + + @Override + public void clear() { + super.clear(); + firstIndex = lastIndex = -1; + } + + @Override + public void clearAndTrim(int size) { + int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); + if(request >= capacity) { + clear(); + return; + } + capacity = request; + mask = request-1; + maxFill = Math.min((int)Math.ceil(capacity * loadFactor), capacity - 1); + for(int i = 0,m=keys.length;i>> 32); + links[lastIndex] |= 0xFFFFFFFFL; + } + else { + long link = links[startPos]; + int prev = (int)(link >>> 32); + int next = (int)link; + links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); + } + links[firstIndex] ^= ((links[firstIndex] ^ ((startPos & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + links[startPos] = 0xFFFFFFFF00000000L | (firstIndex & 0xFFFFFFFFL); + firstIndex = startPos; + } + + protected void moveToLastIndex(int startPos, boolean adding) { + if(size == (adding ? 0 : 1) || lastIndex == startPos) return; + if(firstIndex == startPos) { + firstIndex = (int)links[startPos]; + links[lastIndex] |= 0xFFFFFFFF00000000L; + } + else { + long link = links[startPos]; + int prev = (int)(link >>> 32); + int next = (int)link; + links[prev] ^= ((links[prev] ^ (link & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[next] ^= ((links[next] ^ (link & 0xFFFFFFFF00000000L)) & 0xFFFFFFFF00000000L); + } + links[lastIndex] ^= ((links[lastIndex] ^ (startPos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[startPos] = ((lastIndex & 0xFFFFFFFFL) << 32) | 0xFFFFFFFFL; + lastIndex = startPos; + } + + @Override + protected void onNodeAdded(int pos) { + if(size == 0) { + firstIndex = lastIndex = pos; + links[pos] = -1L; + } + else { + links[lastIndex] ^= ((links[lastIndex] ^ (pos & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + links[pos] = ((lastIndex & 0xFFFFFFFFL) << 32) | 0xFFFFFFFFL; + lastIndex = pos; + } + } + + @Override + 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); + } + } + + @Override + 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; + } + } + + @Override + protected void rehash(int newSize) { + int newMask = newSize - 1; + WeakKey[] newKeys = new WeakKey[newSize + 1]; + VALUE_TYPE[] newValues = NEW_VALUE_ARRAY(newSize + 1); + long[] newLinks = new long[newSize + 1]; + 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(keys[i].hash()) & newMask; + while(KEY_EQUALS_NOT_NULL(newKeys[pos])) pos = ++pos & newMask; + } + newKeys[pos] = keys[i].index(pos); + 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; + capacity = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(capacity * loadFactor), capacity - 1); + keys = newKeys; + values = newValues; + } + + private class MapEntrySet extends AbstractObjectSet implements ORDERED_MAP.FastOrderedSet KEY_VALUE_GENERIC_TYPE { + @Override + public void addFirst(MAP.Entry KEY_VALUE_GENERIC_TYPE o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(MAP.Entry KEY_VALUE_GENERIC_TYPE o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(MAP.Entry KEY_VALUE_GENERIC_TYPE o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(MAP.Entry KEY_VALUE_GENERIC_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(MAP.Entry KEY_VALUE_GENERIC_TYPE o) { + return LINKED_REF_MAP.this.moveToFirst(o.ENTRY_KEY()); + } + + @Override + public boolean moveToLast(MAP.Entry KEY_VALUE_GENERIC_TYPE o) { + return LINKED_REF_MAP.this.moveToLast(o.ENTRY_KEY()); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE getFirst() { + return new BasicEntryKV_BRACES(FIRST_ENTRY_KEY(), FIRST_ENTRY_VALUE()); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE getLast() { + return new BasicEntryKV_BRACES(LAST_ENTRY_KEY(), LAST_ENTRY_VALUE()); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE removeFirst() { + BasicEntry KEY_VALUE_GENERIC_TYPE entry = new BasicEntryKV_BRACES(FIRST_ENTRY_KEY(), FIRST_ENTRY_VALUE()); + POLL_FIRST_ENTRY_KEY(); + return entry; + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE removeLast() { + BasicEntry KEY_VALUE_GENERIC_TYPE entry = new BasicEntryKV_BRACES(LAST_ENTRY_KEY(), LAST_ENTRY_VALUE()); + POLL_LAST_ENTRY_KEY(); + return entry; + } + + @Override + public ObjectBidirectionalIterator iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(MAP.Entry KEY_VALUE_GENERIC_TYPE fromElement) { + return new EntryIterator(fromElement.ENTRY_KEY()); + } + + @Override + public ObjectBidirectionalIterator fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator fastIterator(KEY_TYPE fromElement) { + return new FastEntryIterator(fromElement); + } + + @Override + public MapEntrySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(new ValueMapEntry(index)); + index = (int)links[index]; + } + } + + @Override + public void fastForEach(Consumer action) { + MapEntry entry = new MapEntry(); + int index = firstIndex; + while(index != -1){ + entry.set(index); + action.accept(entry); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + int count = 0; + int index = firstIndex; + while(index != -1) { + action.accept(count++, new ValueMapEntry(index)); + index = (int)links[index]; + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + int index = firstIndex; + while(index != -1) { + action.accept(input, new ValueMapEntry(index)); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + MapEntry entry = new MapEntry(); + int index = firstIndex; + while(index != -1) { + entry.set(index); + if(filter.test(entry)) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + MapEntry entry = new MapEntry(); + int index = firstIndex; + while(index != -1) { + entry.set(index); + if(filter.test(entry)) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + MapEntry entry = new MapEntry(); + int index = firstIndex; + while(index != -1) { + entry.set(index); + if(!filter.test(entry)) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, new ValueMapEntry(index)); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + MAP.Entry KEY_VALUE_GENERIC_TYPE state = null; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = new ValueMapEntry(index); + index = (int)links[index]; + continue; + } + state = operator.apply(state, new ValueMapEntry(index)); + index = (int)links[index]; + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + MapEntry entry = new MapEntry(); + int index = firstIndex; + while(index != -1) { + entry.set(index); + if(filter.test(entry)) return Optional.ofNullable(entry); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + MapEntry entry = new MapEntry(); + int index = firstIndex; + while(index != -1) { + entry.set(index); + if(filter.test(entry)) result++; + index = (int)links[index]; + } + return result; + } + + @Override + @Deprecated + public boolean contains(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; + int index = LINKED_REF_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), LINKED_REF_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = LINKED_REF_MAP.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(LINKED_REF_MAP.this.values[index])); + } + } + 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 LINKED_REF_MAP.this.remove(entry.ENTRY_KEY(), entry.ENTRY_VALUE()); + } + Map.Entry entry = (Map.Entry)o; + return LINKED_REF_MAP.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return LINKED_REF_MAP.this.size(); + } + + @Override + public void clear() { + LINKED_REF_MAP.this.clear(); + } + } + + private final class KeySet extends ABSTRACT_SET KEY_GENERIC_TYPE implements ORDERED_SET KEY_GENERIC_TYPE { +#if TYPE_OBJECT + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + LINKED_REF_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; + LINKED_REF_MAP.this.remove(o); + return size != oldSize; + } + +#endif + @Override + public boolean add(KEY_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(KEY_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(KEY_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(KEY_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(KEY_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(KEY_TYPE o) { + return LINKED_REF_MAP.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(KEY_TYPE o) { + return LINKED_REF_MAP.this.moveToLast(o); + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { + return new KeyIterator(true); + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE reverseIterator() { + return new KeyIterator(false); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return LINKED_REF_MAP.this.size(); + } + + @Override + public void clear() { + LINKED_REF_MAP.this.clear(); + } + + @Override + public KEY_TYPE GET_FIRST_KEY() { + return FIRST_ENTRY_KEY(); + } + + @Override + public KEY_TYPE REMOVE_FIRST_KEY() { + return POLL_FIRST_ENTRY_KEY(); + } + + @Override + public KEY_TYPE GET_LAST_KEY() { + return LAST_ENTRY_KEY(); + } + + @Override + public KEY_TYPE REMOVE_LAST_KEY() { + return POLL_LAST_ENTRY_KEY(); + } + + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + int count = 0; + int index = firstIndex; + while(index != -1){ + action.accept(count++, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + int index = firstIndex; + while(index != -1) { + action.accept(input, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index].get())) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index].get())) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + int index = firstIndex; + while(index != -1) { + if(!filter.test(keys[index].get())) return false; + index = (int)links[index]; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, SINGLE_UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.APPLY_KEY_VALUE(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + +#else + @Override + public KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { + Objects.requireNonNull(operator); + KEY_SPECIAL_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + +#endif + @Override + public OPTIONAL KEY_GENERIC_TYPE reduce(SINGLE_UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_KEY_VALUE; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = keys[index].get(); + index = (int)links[index]; + continue; + } + state = operator.APPLY_KEY_VALUE(state, keys[index].get()); + index = (int)links[index]; + } + return empty ? OPTIONAL.empty() : OPTIONAL.GET_OPTIONAL(state); + } + + @Override + public OPTIONAL KEY_GENERIC_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OPTIONAL.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return OPTIONAL.GET_OPTIONAL(keys[index].get()); + index = (int)links[index]; + } + return OPTIONAL.empty(); + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) result++; + index = (int)links[index]; + } + return result; + } + } + + private class Values extends VALUE_ABSTRACT_COLLECTION VALUE_GENERIC_TYPE implements VALUE_ORDERED_COLLECTION VALUE_GENERIC_TYPE { +#if VALUE_OBJECT + @Override + @Deprecated + 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(true); } + @Override + public int size() { return LINKED_REF_MAP.this.size(); } + @Override + public void clear() { LINKED_REF_MAP.this.clear(); } + @Override + public VALUE_ORDERED_COLLECTION VALUE_GENERIC_TYPE reversed() { return new VALUE_ABSTRACT_COLLECTION.VALUE_REVERSED_ORDERED_COLLECTIONVALUE_BRACES(this, this::reverseIterator); } + private VALUE_ITERATOR VALUE_GENERIC_TYPE reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(VALUE_TYPE e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(VALUE_TYPE e) { throw new UnsupportedOperationException(); } + @Override + public VALUE_TYPE VALUE_GET_FIRST_KEY() { return FIRST_ENTRY_VALUE(); } + @Override + public VALUE_TYPE VALUE_REMOVE_FIRST_KEY() { + VALUE_TYPE result = FIRST_ENTRY_VALUE(); + POLL_FIRST_ENTRY_KEY(); + return result; + } + @Override + public VALUE_TYPE VALUE_GET_LAST_KEY() { return LAST_ENTRY_VALUE(); } + @Override + public VALUE_TYPE VALUE_REMOVE_LAST_KEY() { + VALUE_TYPE result = LAST_ENTRY_VALUE(); + POLL_LAST_ENTRY_KEY(); + return result; + } + @Override + public void forEach(VALUE_CONSUMER VALUE_SUPER_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(VALUE_BI_FROM_INT_CONSUMER VALUE_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + int count = 0; + int index = firstIndex; + while(index != -1){ + action.accept(count++, values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEach(E input, VALUE_BI_FROM_OBJECT_CONSUMER VSV_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + int index = firstIndex; + while(index != -1){ + action.accept(input, values[index]); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public boolean matchesNone(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + int index = firstIndex; + while(index != -1) { + if(filter.test(values[index])) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public boolean matchesAll(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + int index = firstIndex; + while(index != -1) { + if(!filter.test(values[index])) return false; + index = (int)links[index]; + } + return true; + } + +#if !VALUE_OBJECT + @Override + public VALUE_TYPE reduce(VALUE_TYPE identity, VALUE_SINGLE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + VALUE_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.APPLY_VALUE(state, values[index]); + index = (int)links[index]; + } + return state; + } + +#else + @Override + public VALUE_SPECIAL_TYPE reduce(VALUE_SPECIAL_TYPE identity, BiFunction operator) { + Objects.requireNonNull(operator); + VALUE_SPECIAL_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, values[index]); + index = (int)links[index]; + } + return state; + } + +#endif + @Override + public VALUE_OPTIONAL VALUE_GENERIC_TYPE reduce(VALUE_SINGLE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + VALUE_TYPE state = EMPTY_VALUE; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.APPLY_VALUE(state, values[index]); + index = (int)links[index]; + } + return empty ? VALUE_OPTIONAL.empty() : VALUE_OPTIONAL.GET_OPTIONAL_VALUE(state); + } + + @Override + public VALUE_OPTIONAL VALUE_GENERIC_TYPE findFirst(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return VALUE_OPTIONAL.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return VALUE_OPTIONAL.GET_OPTIONAL_VALUE(values[index]); + index = (int)links[index]; + } + return VALUE_OPTIONAL.empty(); + } + + @Override + public int count(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) result++; + index = (int)links[index]; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectListIterator { + MapEntry entry = new MapEntry(); + + public FastEntryIterator(boolean start) { super(start); } + public FastEntryIterator(KEY_TYPE from) { + super(from); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(MAP.Entry KEY_VALUE_GENERIC_TYPE entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(MAP.Entry KEY_VALUE_GENERIC_TYPE entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(KEY_TYPE from) { + super(from); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public MAP.Entry KEY_VALUE_GENERIC_TYPE previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(MAP.Entry KEY_VALUE_GENERIC_TYPE entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(MAP.Entry KEY_VALUE_GENERIC_TYPE entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(KEY_TYPE from) { + super(from); + } + + @Override + public KEY_TYPE PREVIOUS() { + return keys[previousEntry()].get(); + } + + @Override + public KEY_TYPE NEXT() { + return keys[nextEntry()].get(); + } + + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements VALUE_LIST_ITERATOR VALUE_GENERIC_TYPE { + public ValueIterator(boolean start) { super(start); } + + @Override + public VALUE_TYPE VALUE_PREVIOUS() { + return values[previousEntry()]; + } + + @Override + public VALUE_TYPE VALUE_NEXT() { + return values[nextEntry()]; + } + + @Override + public void set(VALUE_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(VALUE_TYPE e) { throw new UnsupportedOperationException(); } + } + + private class MapIterator { + boolean forward; + int previous = -1; + int next = -1; + int current = -1; + int index = 0; + + MapIterator(boolean start) { + this.forward = start; + if(start) next = firstIndex; + else previous = lastIndex; + } + + MapIterator(KEY_TYPE from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(from)) & mask; + WeakKey refKey = null; + while(KEY_EQUALS_NOT_NULL((refKey = keys[pos]))) { + if(KEY_EQUALS(refKey.get(), from)) { + next = (int)links[pos]; + previous = pos; + break; + } + pos = ++pos & mask; + } + if(previous == -1 && next == -1) + throw new NoSuchElementException("The element was not found"); + } + } + + public boolean hasNext() { + return (forward ? next : previous) != -1; + } + + public boolean hasPrevious() { + return (forward ? previous : next) != -1; + } + + public int nextIndex() { + ensureIndexKnown(); + return index; + } + + public int previousIndex() { + ensureIndexKnown(); + return index - 1; + } + + public void remove() { + if(current == -1) throw new IllegalStateException(); + ensureIndexKnown(); + if(current == previous) { + index--; + previous = (int)(links[current] >>> 32); + } + else next = (int)links[current]; + size--; + if(previous == -1) firstIndex = next; + else links[previous] ^= ((links[previous] ^ (next & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + + if(next == -1) lastIndex = previous; + else links[next] ^= ((links[next] ^ ((previous & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + if(current == capacity) { + current = -1; + keys[capacity] = EMPTY_KEY_VALUE; + values[capacity] = EMPTY_VALUE; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey 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(current.hash()) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current.index(last); + values[last] = values[startPos]; + if(next == startPos) next = last; + if(previous == startPos) previous = last; + onNodeMoved(startPos, last); + } + } + } + + public int previousEntry() { + if(!hasPrevious()) throw new NoSuchElementException(); + if(forward) moveBackwards(); + else moveForwards(); + if(index >= 0) index--; + return current; + } + + public int nextEntry() { + if(!hasNext()) throw new NoSuchElementException(); + if(forward) moveForwards(); + else moveBackwards(); + if(index >= 0) index++; + return current; + } + + private void moveBackwards() { + current = previous; + previous = (int)(links[current] >> 32); + next = current; + } + + private void moveForwards() { + current = next; + next = (int)(links[current]); + previous = current; + } + + private void ensureIndexKnown() { + if(index == -1) { + if(previous == -1) { + index = 0; + } + else if(next == -1) { + index = size; + } + else { + index = 1; + for(int pos = firstIndex;pos != previous;pos = (int)links[pos], index++); + } + } + } + + } +} \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/ReferenceHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/ReferenceHashMap.template new file mode 100644 index 0000000..7077cbb --- /dev/null +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/ReferenceHashMap.template @@ -0,0 +1,1572 @@ +package speiger.src.collections.PACKAGE.maps.impl.reference; + +import java.lang.ref.WeakReference; +import java.lang.ref.ReferenceQueue; +import java.util.Arrays; +import java.util.ConcurrentModificationException; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.BiFunction; +import java.util.function.Predicate; +#if !TYPE_OBJECT && JDK_TYPE +import java.util.function.PREDICATE; +import java.util.OPTIONAL; +#endif +#if !SAME_TYPE && JDK_VALUE && !VALUE_OBJECT +import java.util.function.VALUE_PREDICATE; +import java.util.VALUE_OPTIONAL; +#endif + +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.collections.ITERATOR; +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 && !JDK_TYPE +import speiger.src.collections.PACKAGE.functions.OPTIONAL; +#if !VALUE_BOOLEAN +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +#endif +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; +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.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; +import speiger.src.collections.VALUE_PACKAGE.functions.VALUE_OPTIONAL; +#endif +#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; +import speiger.src.collections.utils.ITrimmable; + +/** + * A Type Specific Custom implementation of the HashMap + * Instead of using Wrapper Object Arrays for storing keys and values there is dedicated arrays for storing keys and values. + * Extra to that there is a couple quality of life functions provided + * @Type(T) + * @ValueType(V) + */ +public class REF_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENERIC_TYPE implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient VALUE_TYPE[] values; + /** Minimum array size the HashMap will be */ + protected transient int minCapacity; + /** Current capacity of the HashMap */ + protected transient int capacity; + /** 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; + /** 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 valuesC; + + /** Amount of Elements stored in the HashMap */ + protected int size; + /** How full the Arrays are allowed to get before resize */ + protected final float loadFactor; + + protected final ReferenceQueue queue = new ReferenceQueue<>(); + + /** + * Default Constructor + */ + public REF_MAP() { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * 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 REF_MAP(int minCapacity) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * 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 REF_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 = capacity = HashUtil.arraySize(minCapacity, loadFactor); + mask = this.capacity - 1; + maxFill = Math.min((int)Math.ceil(mask * loadFactor), mask); + keys = new WeakKey[this.capacity]; + values = NEW_VALUE_ARRAY(this.capacity); + } + +#if !TYPE_OBJECT || !VALUE_OBJECT + /** + * Helper constructor that allow to create a map from boxed values (it will unbox them) + * @param keys the keys that should be put into the map + * @param values the values that should be put into the map. + * @throws IllegalStateException if the keys and values do not match in lenght + */ + public REF_MAP(CLASS_TYPE[] keys, CLASS_VALUE_TYPE[] values) { + this(keys, values, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a map from boxed values (it will unbox them) + * @param keys the keys that should be put into the map + * @param values the values that should be put into the map. + * @param loadFactor the percentage of how full the backing array can be before they resize + * @throws IllegalStateException if the keys and values do not match in lenght + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public REF_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); + } + + /** + * 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 REF_MAP(Map map, float loadFactor) { + this(map.size(), loadFactor); + 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 REF_MAP(MAP KEY_VALUE_GENERIC_TYPE map) { + this(map, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * 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 REF_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(); + } + else if(VALUE_EQUALS(values[slot], getDefaultReturnValue())) { + VALUE_TYPE oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + 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; + } + + @Override + public VALUE_TYPE subFrom(KEY_TYPE key, VALUE_TYPE value) { + int slot = findIndex(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; + } +#endif +#if !TYPE_OBJECT + @Override + public boolean containsKey(KEY_TYPE key) { + return findIndex(key) >= 0; + } + +#endif + @Override + @Primitive + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + +#if !VALUE_OBJECT + @Override + public boolean containsValue(VALUE_TYPE value) { + for(int i = capacity-1;i >= 0;i--) + if(KEY_EQUALS_NOT_NULL(keys[i]) && VALUE_EQUALS(values[i], value)) return true; + return false; + } + +#endif + @Override + @ValuePrimitive + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) +#if VALUE_OBJECT + if(KEY_EQUALS_NOT_NULL(keys[i]) && EQUALS_VALUE_TYPE(values[i], value)) return true; +#else + if(KEY_EQUALS_NOT_NULL(keys[i]) && ((value == null && values[i] == getDefaultReturnValue()) || EQUALS_VALUE_TYPE(values[i], value))) return true; +#endif + return false; + } + + @Override + public VALUE_TYPE REMOVE_VALUE(KEY_TYPE key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public VALUE_TYPE REMOVE_VALUEOrDefault(KEY_TYPE key, VALUE_TYPE defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + 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) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(KEY_TO_HASH(key)) & mask; + WeakKey current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(KEY_EQUALS(current.get(), 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.get(), key) && VALUE_EQUALS(value, values[pos])) { + removeIndex(pos); + return true; + } + } + } + +#endif + @Override + public boolean remove(Object key, Object value) { + removeStaleEntries(); + Objects.requireNonNull(key); + Objects.requireNonNull(value); + int pos = HashUtil.mix(key.hashCode()) & mask; + WeakKey current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(EQUALS_KEY_TYPE(current.get(), 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.get(), 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 REF_MAP KEY_VALUE_GENERIC_TYPE copy() { + REF_MAP KEY_VALUE_GENERIC_TYPE map = new REF_MAPKV_BRACES(0, loadFactor); + map.minCapacity = minCapacity; + map.capacity = capacity; + map.mask = mask; + map.maxFill = maxFill; + map.size = size; + map.keys = new WeakKey[keys.length]; + for(int i = 0,m=keys.length;i(keys[i].get(), map.queue).index(i); + } + } + map.values = Arrays.copyOf(values, values.length); + return map; + } + + @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) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(KEY_TYPE key, VALUE_TYPE oldValue, VALUE_TYPE newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public VALUE_TYPE replace(KEY_TYPE key, VALUE_TYPE value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + VALUE_TYPE oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(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; + } + + @Override + public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(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; + } + + @Override + public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(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; + } + + @Override + public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(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; + } + +#if !VALUE_OBJECT + @Override + public VALUE_TYPE COMPUTENonDefault(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(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; + } + + @Override + public VALUE_TYPE COMPUTE_IF_ABSENTNonDefault(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(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; + } + + @Override + public VALUE_TYPE SUPPLY_IF_ABSENTNonDefault(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(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; + } + + @Override + public VALUE_TYPE COMPUTE_IF_PRESENTNonDefault(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(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; + } + +#endif + @Override + public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if VALUE_OBJECT + Objects.requireNonNull(value); +#endif + int index = findIndex(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; + } + + @Override + public void BULK_MERGE(MAP KEY_VALUE_GENERIC_TYPE m, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(MAP.Entry KEY_VALUE_GENERIC_TYPE entry : getFastIterable(m)) { + KEY_TYPE key = entry.ENTRY_KEY(); + int index = findIndex(key); + VALUE_TYPE newValue = index < 0 || VALUE_EQUALS(values[index], getDefaultReturnValue()) ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(values[index], entry.ENTRY_VALUE()); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { + if(index >= 0) + removeIndex(index); + } + else if(index < 0) insert(-index-1, key, newValue); + else values[index] = newValue; + } + } + + @Override + public int size() { return size; } + + @Override + public void clear() { + if(size == 0) return; + size = 0; + for(int i = 0,m=keys.length;i= capacity || this.size >= Math.min((int)Math.ceil(request * loadFactor), request - 1)) return false; + try { + rehash(request); + } + catch(OutOfMemoryError noMemory) { return false; } + return true; + } + + @Override + public void clearAndTrim(int size) { + int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); + if(request >= capacity) { + clear(); + return; + } + capacity = request; + mask = request-1; + maxFill = Math.min((int)Math.ceil(capacity * loadFactor), capacity - 1); + for(int i = 0,m=keys.length;i 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) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(key.hashCode()) & mask; + WeakKey current = keys[pos]; + if(KEY_EQUALS_NOT_NULL(current)) { + if(EQUALS_KEY_TYPE(current.get(), key)) return pos; + while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) + if(EQUALS_KEY_TYPE(current.get(), key)) return pos; + } + return -(pos + 1); + } + + protected VALUE_TYPE removeIndex(int pos) { + VALUE_TYPE value = values[pos]; + keys[pos].index(-1); + keys[pos] = EMPTY_KEY_VALUE; + values[pos] = EMPTY_VALUE; + size--; + onNodeRemoved(pos); + shiftKeys(pos); + if(capacity > minCapacity && size < maxFill / 4 && capacity > HashUtil.DEFAULT_MIN_CAPACITY) rehash(capacity / 2); + return value; + } + + protected void insert(int slot, KEY_TYPE key, VALUE_TYPE value) { + keys[slot] = new WeakKey<>(key, queue).index(slot); + values[slot] = value; + onNodeAdded(slot); + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + } + + protected void rehash(int newSize) { + int newMask = newSize - 1; + WeakKey[] newKeys = new WeakKey[newSize + 1]; + VALUE_TYPE[] newValues = NEW_VALUE_ARRAY(newSize + 1); + for(int i = capacity, pos = 0, j = (size);j-- != 0;) { + while(true) { + if(--i < 0) throw new ConcurrentModificationException("Map was modified during rehash"); + if(KEY_EQUALS_NOT_NULL(keys[i])) break; + } + if(KEY_EQUALS_NOT_NULL(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask])) + while(KEY_EQUALS_NOT_NULL(newKeys[pos = (++pos & newMask)])); + newKeys[pos] = keys[i].index(pos); + newValues[pos] = values[i]; + } + capacity = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(capacity * loadFactor), capacity - 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; + WeakKey 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(current.hash()) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current.index(last); + values[last] = values[startPos]; + onNodeMoved(startPos, last); + } + } + + private void removeStaleEntries() { + for(Object o; (o = queue.poll()) != null;) { + synchronized(queue) { + int index = ((WeakKey)o).index(); + if(index == -1) continue; + removeIndex(index); + } + } + } + + protected static class WeakKey extends WeakReference { + int index; + int hash; + + public WeakKey(T referent, ReferenceQueue q) { + super(referent, q); + this.hash = Objects.hashCode(referent); + } + + public WeakKey index(int index) { + this.index = index; + return this; + } + + public int hash() { return hash; } + public int index() { return index; } + } + + protected class ValueMapEntry extends MapEntry { + protected KEY_TYPE key; + protected VALUE_TYPE value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = 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 { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public KEY_TYPE ENTRY_KEY() { + return keys[index].get(); + } + + @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(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()); + } + } + + 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) { + for(int i = capacity-1;i>=0;i--) + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) { + entry.set(i); + action.accept(entry); + } + } + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(index++, new ValueMapEntry(i)); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(input, new ValueMapEntry(i)); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) { + entry.set(i); + if(filter.test(entry)) return true; + } + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) { + entry.set(i); + if(filter.test(entry)) return false; + } + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + MAP.Entry KEY_VALUE_GENERIC_TYPE state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + if(empty) { + empty = false; + state = new ValueMapEntry(i); + continue; + } + state = operator.apply(state, new ValueMapEntry(i)); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) { + entry.set(i); + if(filter.test(entry)) return Optional.ofNullable(entry); + } + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + MapEntry entry = new MapEntry(); + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return REF_MAP.this.size(); + } + + @Override + public void clear() { + REF_MAP.this.clear(); + } + + @Override + public boolean contains(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; + int index = REF_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), REF_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = REF_MAP.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(REF_MAP.this.values[index])); + } + } + 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 REF_MAP.this.remove(entry.ENTRY_KEY(), entry.ENTRY_VALUE()); + } + Map.Entry entry = (Map.Entry)o; + return REF_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; + REF_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; + REF_MAP.this.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 REF_MAP.this.size(); + } + + @Override + public void clear() { + REF_MAP.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + for(int i = capacity-1;i>=0;i--) + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && !filter.test(keys[i].get())) return false; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, SINGLE_UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.APPLY_KEY_VALUE(state, keys[i].get()); + } + return state; + } + +#else + @Override + public KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { + Objects.requireNonNull(operator); + KEY_SPECIAL_TYPE state = identity; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + +#endif + @Override + public OPTIONAL KEY_GENERIC_TYPE reduce(SINGLE_UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_KEY_VALUE; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.APPLY_KEY_VALUE(state, keys[i].get()); + } + return empty ? OPTIONAL.empty() : OPTIONAL.GET_OPTIONAL(state); + } + + @Override + public OPTIONAL KEY_GENERIC_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OPTIONAL.empty(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i].get())) return OPTIONAL.GET_OPTIONAL(keys[i].get()); + } + return OPTIONAL.empty(); + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i].get())) result++; + } + return result; + } + } + + 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 REF_MAP.this.size(); + } + + @Override + public void clear() { + REF_MAP.this.clear(); + } + + @Override + public void forEach(VALUE_CONSUMER VALUE_SUPER_GENERIC_TYPE action) { + for(int i = capacity-1;i>=0;i--) + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(values[i]); + } + + @Override + public void forEachIndexed(VALUE_BI_FROM_INT_CONSUMER VALUE_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, VALUE_BI_FROM_OBJECT_CONSUMER VSV_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && !filter.test(values[i])) return false; + } + return true; + } + +#if !VALUE_OBJECT + @Override + public VALUE_TYPE reduce(VALUE_TYPE identity, VALUE_SINGLE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + VALUE_TYPE state = identity; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.APPLY_VALUE(state, values[i]); + } + return state; + } + +#else + @Override + public VALUE_SPECIAL_TYPE reduce(VALUE_SPECIAL_TYPE identity, BiFunction operator) { + Objects.requireNonNull(operator); + VALUE_SPECIAL_TYPE state = identity; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.APPLY_VALUE(state, values[i]); + } + return state; + } + +#endif + @Override + public VALUE_OPTIONAL VALUE_GENERIC_TYPE reduce(VALUE_SINGLE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + VALUE_TYPE state = EMPTY_VALUE; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.APPLY_VALUE(state, values[i]); + } + return empty ? VALUE_OPTIONAL.empty() : VALUE_OPTIONAL.GET_OPTIONAL_VALUE(state); + } + + @Override + public VALUE_OPTIONAL VALUE_GENERIC_TYPE findFirst(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return VALUE_OPTIONAL.empty(); + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(values[i])) return VALUE_OPTIONAL.GET_OPTIONAL_VALUE(values[i]); + } + return VALUE_OPTIONAL.empty(); + } + + @Override + public int count(VALUE_PREDICATE VALUE_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(values[i])) result++; + } + return result; + } + } + + 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 ValueMapEntry(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()].get(); + } + } + + 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 = capacity; + int returnedPos = -1; + int lastReturned = -1; + int nextIndex = Integer.MIN_VALUE; + WeakKey[] wrapped = null; + int wrappedIndex = 0; + + public boolean hasNext() { + if(nextIndex == Integer.MIN_VALUE) { + while(true) { + if(--pos < 0) { + if(wrapped == null || wrappedIndex <= -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(); + returnedPos = pos; + if(nextIndex < 0){ + lastReturned = Integer.MAX_VALUE; + int value = wrapped[nextIndex].index(); + 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(returnedPos >= 0) shiftKeys(returnedPos); + else { + REF_MAP.this.remove(wrapped[-returnedPos - 1].get()); + lastReturned = -1; + return; + } + size--; + lastReturned = -1; + } + + private void shiftKeys(int startPos) { + int slot, last; + WeakKey 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(current.hash()) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + if(startPos < last) addWrapper(keys[startPos]); + keys[last] = current.index(last); + values[last] = values[startPos]; + } + } + + private void addWrapper(WeakKey value) { + if(wrapped == null) wrapped = new WeakKey[2]; + else if(wrappedIndex >= wrapped.length) { + WeakKey[] newArray = new WeakKey[wrapped.length * 2]; + System.arraycopy(wrapped, 0, newArray, 0, wrapped.length); + wrapped = newArray; + } + wrapped[wrappedIndex++] = value; + } + } +} \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/utils/Arrays.template b/src/builder/resources/speiger/assets/collections/templates/utils/Arrays.template index 338b181..c991c98 100644 --- a/src/builder/resources/speiger/assets/collections/templates/utils/Arrays.template +++ b/src/builder/resources/speiger/assets/collections/templates/utils/Arrays.template @@ -17,6 +17,7 @@ import java.util.function.IntFunction; #endif import speiger.src.collections.PACKAGE.collections.ITERATOR; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -337,6 +338,69 @@ public class ARRAYS return array; } + /** + * Simple Shuffle method for Arrays. + * With a Callback for indirect shuffling + * @param array the elements that should be shuffled + * @param swapper the callback on the swaps + * @ArrayType(T) + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectShuffle(KEY_TYPE[] array, Swapper swapper) { + return indirectShuffle(array, SanityChecks.getRandom(), swapper); + } + + /** + * Simple Shuffle method for Arrays. + * With a Callback for indirect shuffling + * @param array the elements that should be shuffled + * @param random the Random Number Generator that should be used for the shuffling + * @param swapper the callback on the swaps + * @ArrayType(T) + * @return the provided sorted array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectShuffle(KEY_TYPE[] array, RANDOM random, Swapper swapper) { + return indirectShuffle(array, 0, array.length, random, swapper); + } + + /** + * Simple Shuffle method for Arrays. + * With a Callback for indirect shuffling + * @param array the elements that should be shuffled + * @param length the length of the array + * @param random the Random Number Generator that should be used for the shuffling + * @param swapper the callback on the swaps + * @ArrayType(T) + * @return the provided sorted array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectShuffle(KEY_TYPE[] array, int length, RANDOM random, Swapper swapper) { + return indirectShuffle(array, 0, length, random, swapper); + } + + /** + * Simple Shuffle method for Arrays. + * With a Callback for indirect shuffling + * @param array the elements that should be shuffled + * @param offset the start array + * @param length the length of the array + * @param random the Random Number Generator that should be used for the shuffling + * @param swapper the callback on the swaps + * @ArrayType(T) + * @return the provided sorted array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectShuffle(KEY_TYPE[] array, int offset, int length, RANDOM random, Swapper swapper) { + for(int i = length-1; i>=0;i--) { + int j = offset + i; + int p = offset + random.nextInt(i + 1); + swapper.swap(j, p); + KEY_TYPE t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -575,6 +639,55 @@ public class ARRAYS } } + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Insertion Sort, + * On top of that allows to sort other things along with it. + * @param array the array that needs to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @return input array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectInsertionSort(KEY_TYPE[] array, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectInsertionSort(array, 0, array.length, comp, swapper); + return array; + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Insertion Sort, + * On top of that allows to sort other things along with it. + * @param array the array that needs to be sorted + * @param length the maxmium size of the array to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectInsertionSort(KEY_TYPE[] array, int length, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectInsertionSort(array, 0, length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Insertion Sort, + * On top of that allows to sort other things along with it. + * @param array the array that needs to be sorted + * @param from where the array should be sorted from + * @param to where the array should be sorted to + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectInsertionSort(KEY_TYPE[] array, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + for (int i = from+1;i= from && comp.compare(current, array[j]) < 0) { + swapper.swap(j+1, j); + array[j+1] = array[j--]; + } + array[j+1] = current; + } + } + /** * Sorts an array according to the natural ascending order using InsertionSort, * @param array the array that needs to be sorted @@ -661,6 +774,60 @@ public class ARRAYS } } + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Selection Sort, + * On top of that allows to sort other things along with it. + * @param array the array that needs to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @return input array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectSelectionSort(KEY_TYPE[] array, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectSelectionSort(array, 0, array.length, comp, swapper); + return array; + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Selection Sort, + * On top of that allows to sort other things along with it. + * @param array the array that needs to be sorted + * @param length the maxmium size of the array to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectSelectionSort(KEY_TYPE[] array, int length, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectSelectionSort(array, 0, length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Selection Sort, + * On top of that allows to sort other things along with it. + * @param array the array that needs to be sorted + * @param from where the array should be sorted from + * @param to where the array should be sorted to + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectSelectionSort(KEY_TYPE[] array, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + for (int i = from; i < to; i++) { + KEY_TYPE min = array[i]; + int minId = i; + for(int j = i+1; j < to; j++) { + if(comp.compare(array[j], min) < 0) { + min = array[j]; + minId = j; + } + } + swapper.swap(i, minId); + KEY_TYPE temp = array[i]; + array[i] = min; + array[minId] = temp; + } + } + /** * Sorts an array according to the natural ascending order using Selection Sort, * @param array the array that needs to be sorted @@ -760,6 +927,72 @@ public class ARRAYS } } + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Merge Sort, + * On top of that allows to sort other things along with it. + * This implementation was copied from FastUtil with a couple custom optimizations + * @param array the array that needs to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @return input array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectMergeSort(KEY_TYPE[] array, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectMergeSort(array, null, 0, array.length, comp, swapper); + return array; + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Merge Sort, + * On top of that allows to sort other things along with it. + * This implementation was copied from FastUtil with a couple custom optimizations + * @param array the array that needs to be sorted + * @param length the maxmium size of the array to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectMergeSort(KEY_TYPE[] array, int length, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectMergeSort(array, null, 0, length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Merge Sort, + * On top of that allows to sort other things along with it. + * This implementation was copied from FastUtil with a couple custom optimizations + * @param array the array that needs to be sorted + * @param supp the auxillary array that is used to simplify the sorting + * @param from where the array should be sorted from + * @param to where the array should be sorted to + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectMergeSort(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + if(to - from < BASE_THRESHOLD) { + indirectInsertionSort(array, from, to, comp, swapper); + return; + } + if(supp == null) supp = Arrays.copyOf(array, to); + int mid = (from + to) >>> 1; + indirectMergeSort(supp, array, from, mid, comp, swapper); + indirectMergeSort(supp, array, mid, to, comp, swapper); + if(comp.compare(supp[mid - 1], supp[mid]) <= 0) + { + System.arraycopy(supp, from, array, from, to - from); + return; + } + for(int p = from, q = mid;from < to;from++) { + if(q >= to || p < mid && comp.compare(supp[p], supp[q]) < 0) { + swapper.swap(from, p); + array[from] = supp[p++]; + continue; + } + swapper.swap(from, q); + array[from] = supp[q++]; + } + } + /** * Sorts an array according to the natural ascending order using Merge Sort, * This implementation was copied from FastUtil with a couple custom optimizations @@ -856,6 +1089,56 @@ public class ARRAYS mergeSort(array, supp, from, to, comp); } + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using a Parallel Merge Sort, + * On top of that allows to sort other things along with it. + * This implementation was copied from FastUtil with a couple custom optimizations + * @param array the array that needs to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectParallelMergeSort(KEY_TYPE[] array, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectParallelMergeSort(array, null, 0, array.length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Parallel Merge Sort, + * On top of that allows to sort other things along with it. + * This implementation was copied from FastUtil with a couple custom optimizations + * @param array the array that needs to be sorted + * @param length the maxmium size of the array to be sorted + * @param swapper the callback which elements were swapped + * @param comp the Comparator that decides the sorting order + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectParallelMergeSort(KEY_TYPE[] array, int length, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectParallelMergeSort(array, null, 0, length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Parallel Merge Sort, + * On top of that allows to sort other things along with it. + * This implementation was copied from FastUtil with a couple custom optimizations + * @param array the array that needs to be sorted + * @param supp the auxillary array that is used to simplify the sorting + * @param from where the array should be sorted from + * @param to where the array should be sorted to + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectParallelMergeSort(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwapBRACES(array, supp, from, to, comp, swapper)); + return; + } + indirectMergeSort(array, supp, from, to, comp, swapper); + } + /** * Sorts an array according to the natural ascending order using Parallel Merge Sort, * This implementation was copied from FastUtil with a couple custom optimizations @@ -1209,6 +1492,73 @@ public class ARRAYS if((length = d - c) > 1) quickSort(array, to - length, to, comp); } + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Quick Sort, + * On top of that allows to sort other things along with it. + * This implementation is a custom of FastUtil quicksort but with a different code structure, + * and that sorting Algorithm is based on the tuned quicksort adapted from Jon L. Bentley and M. DouglasMcIlroy, "Engineering a Sort Function", Software: Practice and Experience, 23(11), pages1249−1265, 1993. + * @param array the array that needs to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @return input array + */ + public static GENERIC_KEY_BRACES KEY_TYPE[] indirectQuickSort(KEY_TYPE[] array, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectQuickSort(array, 0, array.length, comp, swapper); + return array; + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Quick Sort, + * On top of that allows to sort other things along with it. + * This implementation is a custom of FastUtil quicksort but with a different code structure, + * and that sorting Algorithm is based on the tuned quicksort adapted from Jon L. Bentley and M. DouglasMcIlroy, "Engineering a Sort Function", Software: Practice and Experience, 23(11), pages1249−1265, 1993. + * @param array the array that needs to be sorted + * @param length the maxmium size of the array to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectQuickSort(KEY_TYPE[] array, int length, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectQuickSort(array, 0, length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Quick Sort, + * On top of that allows to sort other things along with it. + * This implementation is a custom of FastUtil quicksort but with a different code structure, + * and that sorting Algorithm is based on the tuned quicksort adapted from Jon L. Bentley and M. DouglasMcIlroy, "Engineering a Sort Function", Software: Practice and Experience, 23(11), pages1249−1265, 1993. + * @param array the array that needs to be sorted + * @param from where the array should be sorted from + * @param to where the array should be sorted to + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + */ + public static GENERIC_KEY_BRACES void indirectQuickSort(KEY_TYPE[] array, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + KEY_TYPE pivot = array[length > 128 ? subMedium(array, from, from + (length / 2), to - 1, length / 8, comp) : medium(array, from, from + (length / 2), to - 1, comp)]; + int a = from, b = a, c = to - 1, d = c; + for(int compare;;swap(array, b++, c--, swapper)) { + for(;b<=c && (compare = comp.compare(array[b], pivot)) <= 0;b++) { + if(compare == 0) swap(array, a++, b, swapper); + } + for(;c>=b && (compare = comp.compare(array[c], pivot)) >= 0;c--) { + if(compare == 0) swap(array, c, d--, swapper); + } + if(b>c) break; + } + swap(array, from, b, Math.min(a - from, b - a), swapper); + swap(array, b, to, Math.min(d - c, to - d - 1), swapper); + if((length = b - a) > 1) indirectQuickSort(array, from, from + length, comp, swapper); + if((length = d - c) > 1) indirectQuickSort(array, to - length, to, comp, swapper); + } + /** * Sorts an array according to the natural ascending order using Quick Sort, * This implementation is a custom of FastUtil quicksort but with a different code structure, @@ -1313,6 +1663,58 @@ public class ARRAYS quickSort(array, from, to, comp); } + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Parallel Quick Sort, + * On top of that allows to sort other things along with it. + * This implementation is a custom of FastUtil quicksort but with a different code structure, + * and that sorting Algorithm is based on the tuned quicksort adapted from Jon L. Bentley and M. DouglasMcIlroy, "Engineering a Sort Function", Software: Practice and Experience, 23(11), pages1249−1265, 1993. + * @param array the array that needs to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static GENERIC_KEY_BRACES void indirectParallelQuickSort(KEY_TYPE[] array, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectParallelQuickSort(array, 0, array.length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Parallel Quick Sort, + * On top of that allows to sort other things along with it. + * This implementation is a custom of FastUtil quicksort but with a different code structure, + * and that sorting Algorithm is based on the tuned quicksort adapted from Jon L. Bentley and M. DouglasMcIlroy, "Engineering a Sort Function", Software: Practice and Experience, 23(11), pages1249−1265, 1993. + * @param array the array that needs to be sorted + * @param length the maxmium size of the array to be sorted + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static GENERIC_KEY_BRACES void indirectParallelQuickSort(KEY_TYPE[] array, int length, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + indirectParallelQuickSort(array, 0, length, comp, swapper); + } + + /** + * Sorts the specified range of elements according to the order induced by the specified comparator using Parallel Quick Sort, + * On top of that allows to sort other things along with it. + * This implementation is a custom of FastUtil quicksort but with a different code structure, + * and that sorting Algorithm is based on the tuned quicksort adapted from Jon L. Bentley and M. DouglasMcIlroy, "Engineering a Sort Function", Software: Practice and Experience, 23(11), pages1249−1265, 1993. + * @param array the array that needs to be sorted + * @param from where the array should be sorted from + * @param to where the array should be sorted to + * @param comp the Comparator that decides the sorting order + * @param swapper the callback which elements were swapped + * @ArrayType(T) + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static GENERIC_KEY_BRACES void indirectParallelQuickSort(KEY_TYPE[] array, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwapBRACES(array, from, to, comp, swapper)); + return; + } + indirectQuickSort(array, from, to, comp, swapper); + } + /** * Sorts an array according to the natural ascending order using Parallel Quick Sort, * This implementation is a custom of FastUtil quicksort but with a different code structure, @@ -1367,6 +1769,18 @@ public class ARRAYS for(int i = 0;i 128 ? subMedium(array, from, from + (length / 2), to - 1, length / 8, comp) : medium(array, from, from + (length / 2), to - 1, comp)]; + int a = from, b = a, c = to - 1, d = c; + for(int compare;;swap(array, b++, c--, swapper)) { + for(;b<=c && (compare = comp.compare(array[b], pivot)) <= 0;b++) { + if(compare == 0) swap(array, a++, b, swapper); + } + for(;c>=b && (compare = comp.compare(array[c], pivot)) >= 0;c--) { + if(compare == 0) swap(array, c, d--, swapper); + } + if(b>c) break; + } + swap(array, from, b, Math.min(a - from, b - a), swapper); + swap(array, b, to, Math.min(d - c, to - d - 1), swapper); + if(b - a > 1 && d - c > 1) invokeAll(new QuickSortActionCompSwapBRACES(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwapBRACES(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwapBRACES(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwapBRACES(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction KEY_GENERIC_TYPE extends RecursiveAction { private static final long serialVersionUID = 0L; KEY_TYPE[] array; @@ -1474,8 +1927,7 @@ public class ARRAYS int from; int to; - MergeSortAction(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to) - { + MergeSortAction(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1483,8 +1935,7 @@ public class ARRAYS } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1512,8 +1963,7 @@ public class ARRAYS int to; COMPARATOR KEY_GENERIC_TYPE comp; - MergeSortActionComp(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp) - { + MergeSortActionComp(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp) { this.array = array; this.supp = supp; this.from = from; @@ -1522,8 +1972,7 @@ public class ARRAYS } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1543,22 +1992,64 @@ public class ARRAYS } } + static class MergeSortActionCompSwap KEY_GENERIC_TYPE extends RecursiveAction { + private static final long serialVersionUID = 0L; + KEY_TYPE[] array; + KEY_TYPE[] supp; + int from; + int to; + COMPARATOR KEY_GENERIC_TYPE comp; + Swapper swapper; + + MergeSortActionCompSwap(KEY_TYPE[] array, KEY_TYPE[] supp, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp, Swapper swapper) { + this.array = array; + this.supp = supp; + this.from = from; + this.to = to; + this.comp = comp; + this.swapper = swapper; + } + + @Override + protected void compute() { + if(to - from < BASE_THRESHOLD) { + indirectInsertionSort(array, from, to, comp, swapper); + return; + } + if(supp == null) supp = Arrays.copyOf(array, to); + int mid = (from + to) >>> 1; + invokeAll(new MergeSortActionCompSwapBRACES(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwapBRACES(supp, array, mid, to, comp, swapper)); + if(comp.compare(supp[mid - 1], supp[mid]) <= 0) + { + System.arraycopy(supp, from, array, from, to - from); + return; + } + for(int p = from, q = mid;from < to;from++) { + if(q >= to || p < mid && comp.compare(supp[p], supp[q]) < 0) { + swapper.swap(from, p); + array[from] = supp[p++]; + continue; + } + swapper.swap(from, q); + array[from] = supp[q++]; + } + } + } + static class MemFreeMergeSortAction KEY_GENERIC_TYPE extends RecursiveAction { private static final long serialVersionUID = 0L; KEY_TYPE[] array; int from; int to; - MemFreeMergeSortAction(KEY_TYPE[] array, int from, int to) - { + MemFreeMergeSortAction(KEY_TYPE[] array, int from, int to) { this.array = array; this.from = from; this.to = to; } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1604,8 +2095,7 @@ public class ARRAYS int to; COMPARATOR KEY_GENERIC_TYPE comp; - MemFreeMergeSortActionComp(KEY_TYPE[] array, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp) - { + MemFreeMergeSortActionComp(KEY_TYPE[] array, int from, int to, COMPARATOR KEY_GENERIC_TYPE comp) { this.array = array; this.from = from; this.to = to; @@ -1613,8 +2103,7 @@ public class ARRAYS } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/builder/resources/speiger/assets/testers/templates/impl/maps/MapConstructorTests.template b/src/builder/resources/speiger/assets/testers/templates/impl/maps/MapConstructorTests.template index 82217fb..1b7a0d9 100644 --- a/src/builder/resources/speiger/assets/testers/templates/impl/maps/MapConstructorTests.template +++ b/src/builder/resources/speiger/assets/testers/templates/impl/maps/MapConstructorTests.template @@ -27,6 +27,8 @@ import speiger.src.collections.PACKAGE.maps.impl.concurrent.CONCURRENT_HASH_MAP; #if TYPE_OBJECT import speiger.src.collections.PACKAGE.maps.impl.misc.ENUM_MAP; import speiger.src.collections.PACKAGE.maps.impl.misc.LINKED_ENUM_MAP; +import speiger.src.collections.PACKAGE.maps.impl.reference.REF_MAP; +import speiger.src.collections.PACKAGE.maps.impl.reference.LINKED_REF_MAP; #endif import speiger.src.collections.PACKAGE.maps.impl.tree.RB_TREE_MAP; @@ -134,6 +136,97 @@ public class MAP_CONSTRUCTOR_TESTS } #if TYPE_OBJECT + public static class RefMap extends FILE_KEY_TYPE2FILE_VALUE_TYPEMapConstructorTester KEY_VALUE_STRING_GENERIC_TYPE + { + public RefMap() { + setSimpleConstructor(REF_MAP::new); + setSizeConstructor(REF_MAP::new); + setPArrayConstructor(REF_MAP::new); +#if !TYPE_OBJECT || !VALUE_OBJECT + setArrayConstructor(REF_MAP::new); +#endif + setPMapConstructor(REF_MAP::new); + setMapConstructor(REF_MAP::new); + } + + @Test + public void testWrongLoadFactorSize() { + setSizeConstructor(T -> new REF_MAP KEY_VALUE_STRING_GENERIC_TYPE(T, 0F)); + try { + testSizeConstructor_smallSize(); + fail("A Constructor using a 0 LoadFactor should error"); + } catch(IllegalStateException e) {} + + setSizeConstructor(T -> new REF_MAP KEY_VALUE_STRING_GENERIC_TYPE(T, 1F)); + try { + testSizeConstructor_smallSize(); + fail("A Constructor using a 1 LoadFactor should error"); + } catch(IllegalStateException e) {} + } + +#if TYPE_OBJECT + @Override + protected String[] createKeyElements() { + return Arrays.copyOfRange(StringSortTest.NAMES, 0, 100); + } + +#endif +#if VALUE_OBJECT + @Override + protected String[] createValueElements() { + return Arrays.copyOfRange(StringSortTest.NAMES, 100, 200); + } + +#endif + } + + public static class LinkedRefMap extends FILE_KEY_TYPE2FILE_VALUE_TYPEMapConstructorTester KEY_VALUE_STRING_GENERIC_TYPE + { + public LinkedRefMap() { + setSimpleConstructor(LINKED_REF_MAP::new); + setSizeConstructor(LINKED_REF_MAP::new); + setPArrayConstructor(LINKED_REF_MAP::new); +#if !TYPE_OBJECT || !VALUE_OBJECT + setArrayConstructor(LINKED_REF_MAP::new); +#endif + setPMapConstructor(LINKED_REF_MAP::new); + setMapConstructor(LINKED_REF_MAP::new); + } + + @Test + public void testWrongLoadFactorSize() { + setSizeConstructor(T -> new LINKED_REF_MAP KEY_VALUE_STRING_GENERIC_TYPE(T, 0F)); + try { + testSizeConstructor_smallSize(); + fail("A Constructor using a 0 LoadFactor should error"); + } catch(IllegalStateException e) { + } + + setSizeConstructor(T -> new LINKED_REF_MAP KEY_VALUE_STRING_GENERIC_TYPE(T, 1F)); + try { + testSizeConstructor_smallSize(); + fail("A Constructor using a 1 LoadFactor should error"); + } catch(IllegalStateException e) { + } + } + +#if TYPE_OBJECT + @Override + protected String[] createKeyElements() { + return Arrays.copyOfRange(StringSortTest.NAMES, 0, 100); + } + +#endif +#if VALUE_OBJECT + @Override + protected String[] createValueElements() { + return Arrays.copyOfRange(StringSortTest.NAMES, 100, 200); + } + +#endif + } + + #if VALUE_OBJECT public static class EnumMap extends FILE_KEY_TYPE2FILE_VALUE_TYPEMapConstructorTester #else diff --git a/src/builder/resources/speiger/assets/tests/templates/maps/MapTests.template b/src/builder/resources/speiger/assets/tests/templates/maps/MapTests.template index 1a5639d..fb8bd89 100644 --- a/src/builder/resources/speiger/assets/tests/templates/maps/MapTests.template +++ b/src/builder/resources/speiger/assets/tests/templates/maps/MapTests.template @@ -41,6 +41,8 @@ import speiger.src.collections.PACKAGE.maps.impl.misc.ARRAY_MAP; #if TYPE_OBJECT import speiger.src.collections.PACKAGE.maps.impl.misc.ENUM_MAP; import speiger.src.collections.PACKAGE.maps.impl.misc.LINKED_ENUM_MAP; +import speiger.src.collections.PACKAGE.maps.impl.reference.REF_MAP; +import speiger.src.collections.PACKAGE.maps.impl.reference.LINKED_REF_MAP; #endif import speiger.src.collections.PACKAGE.maps.impl.tree.AVL_TREE_MAP; import speiger.src.collections.PACKAGE.maps.impl.tree.RB_TREE_MAP; @@ -104,6 +106,7 @@ public class MAP_TESTS extends TestCase #if TYPE_OBJECT constructors.addTest(new TestSuite(MAP_CONSTRUCTOR_TESTS.EnumMap.class)); constructors.addTest(new TestSuite(MAP_CONSTRUCTOR_TESTS.LinkedEnumMap.class)); + constructors.addTest(new TestSuite(MAP_CONSTRUCTOR_TESTS.RefMap.class)); #endif constructors.addTest(new TestSuite(MAP_CONSTRUCTOR_TESTS.CustomHashMap.class)); constructors.addTest(new TestSuite(MAP_CONSTRUCTOR_TESTS.LinkedCustomHashMap.class)); @@ -130,27 +133,29 @@ public class MAP_TESTS extends TestCase } public static void suite(TestSuite suite) { - suite.addTest(mapSuite("HASH_MAP", HASH_MAP::new, getFeatures(), -1, true)); - suite.addTest(orderedMapSuite("LINKED_HASH_MAP", LINKED_HASH_MAP::new, getFeatures(), -1)); - suite.addTest(orderedMapSuite("IMMUTABLE_HASH_MAP", IMMUTABLE_HASH_MAP::new, getImmutableFeatures(), -1)); + suite.addTest(mapSuite("HASH_MAP", HASH_MAP::new, getFeatures(), -1, true, true)); + suite.addTest(orderedMapSuite("LINKED_HASH_MAP", LINKED_HASH_MAP::new, getFeatures(), -1, true)); + suite.addTest(orderedMapSuite("IMMUTABLE_HASH_MAP", IMMUTABLE_HASH_MAP::new, getImmutableFeatures(), -1, true)); #if TYPE_OBJECT suite.addTest(enumMapSuite("ENUM_MAP", ENUM_MAP::new, getFeatures(), 5)); suite.addTest(enumOrderedMapSuite("LINKED_ENUM_MAP", (K, V) -> K.length <= 0 ? new LINKED_ENUM_MAP<>(AnEnum.class) : new LINKED_ENUM_MAP<>(K, V), getFeatures(), 5)); + suite.addTest(mapSuite("REF_MAP", REF_MAP::new, getFeatures(), - 1, true, false)); + suite.addTest(orderedMapSuite("LINKED_REF_MAP", LINKED_REF_MAP::new, getFeatures(), - 1, false)); #endif - suite.addTest(mapSuite("CUSTOM_HASH_MAP", (K, V) -> new CUSTOM_HASH_MAPKV_BRACES(K, V, HashStrategy.INSTANCE), getFeatures(), -1, true)); - suite.addTest(orderedMapSuite("LINKED_CUSTOM_HASH_MAP", (K, V) -> new LINKED_CUSTOM_HASH_MAPKV_BRACES(K, V, HashStrategy.INSTANCE), getFeatures(), -1)); - suite.addTest(orderedMapSuite("ARRAY_MAP", ARRAY_MAP::new, getFeatures(), -1)); + suite.addTest(mapSuite("CUSTOM_HASH_MAP", (K, V) -> new CUSTOM_HASH_MAPKV_BRACES(K, V, HashStrategy.INSTANCE), getFeatures(), -1, true, true)); + suite.addTest(orderedMapSuite("LINKED_CUSTOM_HASH_MAP", (K, V) -> new LINKED_CUSTOM_HASH_MAPKV_BRACES(K, V, HashStrategy.INSTANCE), getFeatures(), -1, true)); + suite.addTest(orderedMapSuite("ARRAY_MAP", ARRAY_MAP::new, getFeatures(), -1, true)); suite.addTest(concurrentMapSuite("CONCURRENT_HASH_MAP", CONCURRENT_HASH_MAP::new, getFeatures(), 2)); suite.addTest(concurrentMapSuite("CONCURRENT_HASH_MAP", CONCURRENT_HASH_MAP::new, getFeatures(), 3)); suite.addTest(navigableMapSuite("RB_TREE_MAP", RB_TREE_MAP::new, getFeatures(), -1)); suite.addTest(navigableMapSuite("AVL_TREE_MAP", AVL_TREE_MAP::new, getFeatures(), -1)); suite.addTest(navigableMapSuite("SynchronizedRB_TREE_MAP", (K, V) -> new RB_TREE_MAPKV_BRACES(K, V).synchronize(), getLimitedFeatures(), -1)); suite.addTest(navigableMapSuite("UnmodifiableRB_TREE_MAP", (K, V) -> new RB_TREE_MAPKV_BRACES(K, V).unmodifiable(), getLimitedImmutableFeatures(), -1)); - suite.addTest(orderedMapSuite("SynchronizedORDERED_MAP", (K, V) -> new LINKED_HASH_MAPKV_BRACES(K, V).synchronize(), getFeatures(), -1)); - suite.addTest(orderedMapSuite("UnmodifiableORDERED_MAP", (K, V) -> new LINKED_HASH_MAPKV_BRACES(K, V).unmodifiable(), getImmutableFeatures(), -1)); - suite.addTest(mapSuite("EmptyMAP", (K, V) -> MAPS.empty(), getEmptyFeatures(), 0, false)); - suite.addTest(mapSuite("SingletonMAP", (K, V) -> MAPS.singleton(K[0], V[0]), getEmptyFeatures(), 1, false)); - suite.addTest(mapSuite("AbstractMap", SIMPLE_TEST_MAP::new, getTestFeatures(), -1, false)); + suite.addTest(orderedMapSuite("SynchronizedORDERED_MAP", (K, V) -> new LINKED_HASH_MAPKV_BRACES(K, V).synchronize(), getFeatures(), -1, true)); + suite.addTest(orderedMapSuite("UnmodifiableORDERED_MAP", (K, V) -> new LINKED_HASH_MAPKV_BRACES(K, V).unmodifiable(), getImmutableFeatures(), -1, true)); + suite.addTest(mapSuite("EmptyMAP", (K, V) -> MAPS.empty(), getEmptyFeatures(), 0, false, true)); + suite.addTest(mapSuite("SingletonMAP", (K, V) -> MAPS.singleton(K[0], V[0]), getEmptyFeatures(), 1, false, true)); + suite.addTest(mapSuite("AbstractMap", SIMPLE_TEST_MAP::new, getTestFeatures(), -1, false, true)); } #if TYPE_OBJECT @@ -209,14 +214,14 @@ public class MAP_TESTS extends TestCase #endif - private static Test mapSuite(String name, BiFunction factory, Collection> features, int size, boolean sorted) { + private static Test mapSuite(String name, BiFunction factory, Collection> features, int size, boolean sorted, boolean allowNullKeys) { SIMPLE_MAP_TEST_GENERATOR.Maps KEY_VALUE_STRING_GENERIC_TYPE generator = new SIMPLE_MAP_TEST_GENERATOR.Maps KEY_VALUE_STRING_GENERIC_TYPE(factory); MAP_TEST_BUILDER KEY_VALUE_STRING_GENERIC_TYPE builder = MAP_TEST_BUILDER.using(generator); builder.shouldBlockKeys(sorted); #if TYPE_OBJECT generator.setKeys(createKeys()); #ignore - builder.withFeatures(MapFeature.ALLOWS_NULL_KEYS); + if(allowNullKeys) builder.withFeatures(MapFeature.ALLOWS_NULL_KEYS); #endignore #endif #if VALUE_OBJECT @@ -265,13 +270,13 @@ public class MAP_TESTS extends TestCase return builder.named(name).createTestSuite(); } - private static Test orderedMapSuite(String name, BiFunction factory, Collection> features, int size) { + private static Test orderedMapSuite(String name, BiFunction factory, Collection> features, int size, boolean allowNullKeys) { SIMPLE_MAP_TEST_GENERATOR.OrderedMaps KEY_VALUE_STRING_GENERIC_TYPE generator = new SIMPLE_MAP_TEST_GENERATOR.OrderedMaps KEY_VALUE_STRING_GENERIC_TYPE(factory); ORDERED_MAP_TEST_BUILDER KEY_VALUE_STRING_GENERIC_TYPE builder = ORDERED_MAP_TEST_BUILDER.using(generator); #if TYPE_OBJECT generator.setKeys(createKeys()); #ignore - builder.withFeatures(MapFeature.ALLOWS_NULL_KEYS); + if(allowNullKeys) builder.withFeatures(MapFeature.ALLOWS_NULL_KEYS); #endignore #endif #if VALUE_OBJECT diff --git a/src/main/java/speiger/src/collections/utils/Swapper.java b/src/main/java/speiger/src/collections/utils/Swapper.java new file mode 100644 index 0000000..9dd39fe --- /dev/null +++ b/src/main/java/speiger/src/collections/utils/Swapper.java @@ -0,0 +1,5 @@ +package speiger.src.collections.utils; + +public interface Swapper { + public void swap(int fromIndex, int toIndex); +}