From e56e1364590ceb6c2f357d587e76beacc6e0aa08 Mon Sep 17 00:00:00 2001 From: Speiger Date: Thu, 14 May 2026 19:24:20 +0200 Subject: [PATCH] Sync with latest changes --- build.gradle | 2 +- .../src/builder/modules/MapModule.java | 18 +- .../maps/impl/hash/LinkedOpenHashMap.template | 4 +- .../reference/LinkedReferenceHashMap.template | 1581 +++++++++++++++++ .../impl/reference/ReferenceHashMap.template | 1572 ++++++++++++++++ .../templates/utils/Arrays.template | 537 +++++- .../impl/maps/MapConstructorTests.template | 93 + .../tests/templates/maps/MapTests.template | 35 +- .../booleans/utils/BooleanArrays.java | 515 +++++- .../hash/Byte2BooleanLinkedOpenHashMap.java | 4 +- .../impl/hash/Byte2ByteLinkedOpenHashMap.java | 4 +- .../impl/hash/Byte2CharLinkedOpenHashMap.java | 4 +- .../hash/Byte2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Byte2FloatLinkedOpenHashMap.java | 4 +- .../impl/hash/Byte2IntLinkedOpenHashMap.java | 4 +- .../impl/hash/Byte2LongLinkedOpenHashMap.java | 4 +- .../hash/Byte2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Byte2ShortLinkedOpenHashMap.java | 4 +- .../collections/bytes/utils/ByteArrays.java | 515 +++++- .../hash/Char2BooleanLinkedOpenHashMap.java | 4 +- .../impl/hash/Char2ByteLinkedOpenHashMap.java | 4 +- .../impl/hash/Char2CharLinkedOpenHashMap.java | 4 +- .../hash/Char2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Char2FloatLinkedOpenHashMap.java | 4 +- .../impl/hash/Char2IntLinkedOpenHashMap.java | 4 +- .../impl/hash/Char2LongLinkedOpenHashMap.java | 4 +- .../hash/Char2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Char2ShortLinkedOpenHashMap.java | 4 +- .../collections/chars/utils/CharArrays.java | 515 +++++- .../hash/Double2BooleanLinkedOpenHashMap.java | 4 +- .../hash/Double2ByteLinkedOpenHashMap.java | 4 +- .../hash/Double2CharLinkedOpenHashMap.java | 4 +- .../hash/Double2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Double2FloatLinkedOpenHashMap.java | 4 +- .../hash/Double2IntLinkedOpenHashMap.java | 4 +- .../hash/Double2LongLinkedOpenHashMap.java | 4 +- .../hash/Double2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Double2ShortLinkedOpenHashMap.java | 4 +- .../doubles/utils/DoubleArrays.java | 515 +++++- .../hash/Float2BooleanLinkedOpenHashMap.java | 4 +- .../hash/Float2ByteLinkedOpenHashMap.java | 4 +- .../hash/Float2CharLinkedOpenHashMap.java | 4 +- .../hash/Float2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Float2FloatLinkedOpenHashMap.java | 4 +- .../impl/hash/Float2IntLinkedOpenHashMap.java | 4 +- .../hash/Float2LongLinkedOpenHashMap.java | 4 +- .../hash/Float2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Float2ShortLinkedOpenHashMap.java | 4 +- .../collections/floats/utils/FloatArrays.java | 515 +++++- .../hash/Int2BooleanLinkedOpenHashMap.java | 4 +- .../impl/hash/Int2ByteLinkedOpenHashMap.java | 4 +- .../impl/hash/Int2CharLinkedOpenHashMap.java | 4 +- .../hash/Int2DoubleLinkedOpenHashMap.java | 4 +- .../impl/hash/Int2FloatLinkedOpenHashMap.java | 4 +- .../impl/hash/Int2IntLinkedOpenHashMap.java | 4 +- .../impl/hash/Int2LongLinkedOpenHashMap.java | 4 +- .../hash/Int2ObjectLinkedOpenHashMap.java | 4 +- .../impl/hash/Int2ShortLinkedOpenHashMap.java | 4 +- .../src/collections/ints/utils/IntArrays.java | 515 +++++- .../hash/Long2BooleanLinkedOpenHashMap.java | 4 +- .../impl/hash/Long2ByteLinkedOpenHashMap.java | 4 +- .../impl/hash/Long2CharLinkedOpenHashMap.java | 4 +- .../hash/Long2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Long2FloatLinkedOpenHashMap.java | 4 +- .../impl/hash/Long2IntLinkedOpenHashMap.java | 4 +- .../impl/hash/Long2LongLinkedOpenHashMap.java | 4 +- .../hash/Long2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Long2ShortLinkedOpenHashMap.java | 4 +- .../collections/longs/utils/LongArrays.java | 515 +++++- .../hash/Object2BooleanLinkedOpenHashMap.java | 4 +- .../hash/Object2ByteLinkedOpenHashMap.java | 4 +- .../hash/Object2CharLinkedOpenHashMap.java | 4 +- .../hash/Object2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Object2FloatLinkedOpenHashMap.java | 4 +- .../hash/Object2IntLinkedOpenHashMap.java | 4 +- .../hash/Object2LongLinkedOpenHashMap.java | 4 +- .../hash/Object2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Object2ShortLinkedOpenHashMap.java | 4 +- .../reference/Reference2BooleanHashMap.java | 1350 ++++++++++++++ .../Reference2BooleanLinkedHashMap.java | 1474 +++++++++++++++ .../impl/reference/Reference2ByteHashMap.java | 1372 ++++++++++++++ .../Reference2ByteLinkedHashMap.java | 1474 +++++++++++++++ .../impl/reference/Reference2CharHashMap.java | 1372 ++++++++++++++ .../Reference2CharLinkedHashMap.java | 1474 +++++++++++++++ .../reference/Reference2DoubleHashMap.java | 1372 ++++++++++++++ .../Reference2DoubleLinkedHashMap.java | 1474 +++++++++++++++ .../reference/Reference2FloatHashMap.java | 1372 ++++++++++++++ .../Reference2FloatLinkedHashMap.java | 1474 +++++++++++++++ .../impl/reference/Reference2IntHashMap.java | 1372 ++++++++++++++ .../reference/Reference2IntLinkedHashMap.java | 1474 +++++++++++++++ .../impl/reference/Reference2LongHashMap.java | 1372 ++++++++++++++ .../Reference2LongLinkedHashMap.java | 1474 +++++++++++++++ .../reference/Reference2ObjectHashMap.java | 1241 +++++++++++++ .../Reference2ObjectLinkedHashMap.java | 1433 +++++++++++++++ .../reference/Reference2ShortHashMap.java | 1372 ++++++++++++++ .../Reference2ShortLinkedHashMap.java | 1474 +++++++++++++++ .../objects/utils/ObjectArrays.java | 537 +++++- .../hash/Short2BooleanLinkedOpenHashMap.java | 4 +- .../hash/Short2ByteLinkedOpenHashMap.java | 4 +- .../hash/Short2CharLinkedOpenHashMap.java | 4 +- .../hash/Short2DoubleLinkedOpenHashMap.java | 4 +- .../hash/Short2FloatLinkedOpenHashMap.java | 4 +- .../impl/hash/Short2IntLinkedOpenHashMap.java | 4 +- .../hash/Short2LongLinkedOpenHashMap.java | 4 +- .../hash/Short2ObjectLinkedOpenHashMap.java | 4 +- .../hash/Short2ShortLinkedOpenHashMap.java | 4 +- .../collections/shorts/utils/ShortArrays.java | 515 +++++- .../src/collections/utils/Swapper.java | 5 + 108 files changed, 33809 insertions(+), 403 deletions(-) create mode 100644 src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/LinkedReferenceHashMap.template create mode 100644 src/builder/resources/speiger/assets/collections/templates/maps/impl/reference/ReferenceHashMap.template create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortHashMap.java create mode 100644 src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortLinkedHashMap.java create mode 100644 src/main/java/speiger/src/collections/utils/Swapper.java diff --git a/build.gradle b/build.gradle index 71f28aa..dd458bb 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,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/booleans/utils/BooleanArrays.java b/src/main/java/speiger/src/collections/booleans/utils/BooleanArrays.java index 838d846..27ec4eb 100644 --- a/src/main/java/speiger/src/collections/booleans/utils/BooleanArrays.java +++ b/src/main/java/speiger/src/collections/booleans/utils/BooleanArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.booleans.functions.BooleanComparator; import speiger.src.collections.booleans.collections.BooleanIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class BooleanArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static boolean[] indirectShuffle(boolean[] 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 + * @return the provided sorted array + */ + public static boolean[] indirectShuffle(boolean[] 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 + * @return the provided sorted array + */ + public static boolean[] indirectShuffle(boolean[] 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 + * @return the provided sorted array + */ + public static boolean[] indirectShuffle(boolean[] 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); + boolean t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class BooleanArrays } } + /** + * 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 + * @return input array + */ + public static boolean[] indirectInsertionSort(boolean[] array, BooleanComparator 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 + */ + public static void indirectInsertionSort(boolean[] array, int length, BooleanComparator 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 + */ + public static void indirectInsertionSort(boolean[] array, int from, int to, BooleanComparator 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 @@ -569,6 +675,57 @@ public class BooleanArrays } } + /** + * 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 + * @return input array + */ + public static boolean[] indirectSelectionSort(boolean[] array, BooleanComparator 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 + */ + public static void indirectSelectionSort(boolean[] array, int length, BooleanComparator 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 + */ + public static void indirectSelectionSort(boolean[] array, int from, int to, BooleanComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + boolean 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); + boolean 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 @@ -662,6 +819,69 @@ public class BooleanArrays } } + /** + * 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 + * @return input array + */ + public static boolean[] indirectMergeSort(boolean[] array, BooleanComparator 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 + */ + public static void indirectMergeSort(boolean[] array, int length, BooleanComparator 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 + */ + public static void indirectMergeSort(boolean[] array, boolean[] supp, int from, int to, BooleanComparator 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 @@ -752,6 +972,53 @@ public class BooleanArrays 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 + */ + public static void indirectParallelMergeSort(boolean[] array, BooleanComparator 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 + */ + public static void indirectParallelMergeSort(boolean[] array, int length, BooleanComparator 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 + */ + public static void indirectParallelMergeSort(boolean[] array, boolean[] supp, int from, int to, BooleanComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class BooleanArrays 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 + * @return input array + */ + public static boolean[] indirectQuickSort(boolean[] array, BooleanComparator 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 + */ + public static void indirectQuickSort(boolean[] array, int length, BooleanComparator 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 + */ + public static void indirectQuickSort(boolean[] array, int from, int to, BooleanComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + boolean 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, @@ -1185,6 +1516,55 @@ public class BooleanArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(boolean[] array, BooleanComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(boolean[] array, int length, BooleanComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(boolean[] array, int from, int to, BooleanComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class BooleanArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; boolean[] array; @@ -1343,8 +1774,7 @@ public class BooleanArrays int from; int to; - MergeSortAction(boolean[] array, boolean[] supp, int from, int to) - { + MergeSortAction(boolean[] array, boolean[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class BooleanArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class BooleanArrays int to; BooleanComparator comp; - MergeSortActionComp(boolean[] array, boolean[] supp, int from, int to, BooleanComparator comp) - { + MergeSortActionComp(boolean[] array, boolean[] supp, int from, int to, BooleanComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class BooleanArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class BooleanArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + boolean[] array; + boolean[] supp; + int from; + int to; + BooleanComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(boolean[] array, boolean[] supp, int from, int to, BooleanComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; boolean[] array; int from; int to; - MemFreeMergeSortAction(boolean[] array, int from, int to) - { + MemFreeMergeSortAction(boolean[] 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; @@ -1473,8 +1942,7 @@ public class BooleanArrays int to; BooleanComparator comp; - MemFreeMergeSortActionComp(boolean[] array, int from, int to, BooleanComparator comp) - { + MemFreeMergeSortActionComp(boolean[] array, int from, int to, BooleanComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class BooleanArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2BooleanLinkedOpenHashMap.java index 4d1962e..8be1d36 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2BooleanLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2BooleanLinkedOpenHashMap extends Byte2BooleanOpenHashMap imple } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2BooleanLinkedOpenHashMap extends Byte2BooleanOpenHashMap imple } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ByteLinkedOpenHashMap.java index 1117cc3..d3885a5 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ByteLinkedOpenHashMap.java @@ -241,7 +241,7 @@ public class Byte2ByteLinkedOpenHashMap extends Byte2ByteOpenHashMap implements } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -265,7 +265,7 @@ public class Byte2ByteLinkedOpenHashMap extends Byte2ByteOpenHashMap implements } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2CharLinkedOpenHashMap.java index a3f2091..e023870 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2CharLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2CharLinkedOpenHashMap extends Byte2CharOpenHashMap implements } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2CharLinkedOpenHashMap extends Byte2CharOpenHashMap implements } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2DoubleLinkedOpenHashMap.java index d485dfe..98065ba 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2DoubleLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2DoubleLinkedOpenHashMap extends Byte2DoubleOpenHashMap impleme } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2DoubleLinkedOpenHashMap extends Byte2DoubleOpenHashMap impleme } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2FloatLinkedOpenHashMap.java index 22fd885..600b401 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2FloatLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2FloatLinkedOpenHashMap extends Byte2FloatOpenHashMap implement } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2FloatLinkedOpenHashMap extends Byte2FloatOpenHashMap implement } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2IntLinkedOpenHashMap.java index 9a37ddc..88f0a9f 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2IntLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2IntLinkedOpenHashMap extends Byte2IntOpenHashMap implements By } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2IntLinkedOpenHashMap extends Byte2IntOpenHashMap implements By } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2LongLinkedOpenHashMap.java index 3cbc4c6..b9622de 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2LongLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2LongLinkedOpenHashMap extends Byte2LongOpenHashMap implements } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2LongLinkedOpenHashMap extends Byte2LongOpenHashMap implements } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ObjectLinkedOpenHashMap.java index 74d4ef5..7a60173 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ObjectLinkedOpenHashMap.java @@ -243,7 +243,7 @@ public class Byte2ObjectLinkedOpenHashMap extends Byte2ObjectOpenHashMap i } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -267,7 +267,7 @@ public class Byte2ObjectLinkedOpenHashMap extends Byte2ObjectOpenHashMap i } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ShortLinkedOpenHashMap.java index d574a57..96cbeda 100644 --- a/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/bytes/maps/impl/hash/Byte2ShortLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Byte2ShortLinkedOpenHashMap extends Byte2ShortOpenHashMap implement } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Byte2ShortLinkedOpenHashMap extends Byte2ShortOpenHashMap implement } else { int pos = HashUtil.mix(Byte.hashCode(key)) & mask; - while(key == (byte)0) { + while(keys[pos] != (byte)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/bytes/utils/ByteArrays.java b/src/main/java/speiger/src/collections/bytes/utils/ByteArrays.java index ec7ec41..b0de71c 100644 --- a/src/main/java/speiger/src/collections/bytes/utils/ByteArrays.java +++ b/src/main/java/speiger/src/collections/bytes/utils/ByteArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.bytes.functions.ByteComparator; import speiger.src.collections.bytes.collections.ByteIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class ByteArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static byte[] indirectShuffle(byte[] 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 + * @return the provided sorted array + */ + public static byte[] indirectShuffle(byte[] 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 + * @return the provided sorted array + */ + public static byte[] indirectShuffle(byte[] 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 + * @return the provided sorted array + */ + public static byte[] indirectShuffle(byte[] 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); + byte t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class ByteArrays } } + /** + * 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 + * @return input array + */ + public static byte[] indirectInsertionSort(byte[] array, ByteComparator 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 + */ + public static void indirectInsertionSort(byte[] array, int length, ByteComparator 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 + */ + public static void indirectInsertionSort(byte[] array, int from, int to, ByteComparator 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 @@ -569,6 +675,57 @@ public class ByteArrays } } + /** + * 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 + * @return input array + */ + public static byte[] indirectSelectionSort(byte[] array, ByteComparator 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 + */ + public static void indirectSelectionSort(byte[] array, int length, ByteComparator 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 + */ + public static void indirectSelectionSort(byte[] array, int from, int to, ByteComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + byte 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); + byte 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 @@ -662,6 +819,69 @@ public class ByteArrays } } + /** + * 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 + * @return input array + */ + public static byte[] indirectMergeSort(byte[] array, ByteComparator 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 + */ + public static void indirectMergeSort(byte[] array, int length, ByteComparator 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 + */ + public static void indirectMergeSort(byte[] array, byte[] supp, int from, int to, ByteComparator 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 @@ -752,6 +972,53 @@ public class ByteArrays 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 + */ + public static void indirectParallelMergeSort(byte[] array, ByteComparator 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 + */ + public static void indirectParallelMergeSort(byte[] array, int length, ByteComparator 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 + */ + public static void indirectParallelMergeSort(byte[] array, byte[] supp, int from, int to, ByteComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class ByteArrays 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 + * @return input array + */ + public static byte[] indirectQuickSort(byte[] array, ByteComparator 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 + */ + public static void indirectQuickSort(byte[] array, int length, ByteComparator 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 + */ + public static void indirectQuickSort(byte[] array, int from, int to, ByteComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + byte 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, @@ -1185,6 +1516,55 @@ public class ByteArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(byte[] array, ByteComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(byte[] array, int length, ByteComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(byte[] array, int from, int to, ByteComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class ByteArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; byte[] array; @@ -1343,8 +1774,7 @@ public class ByteArrays int from; int to; - MergeSortAction(byte[] array, byte[] supp, int from, int to) - { + MergeSortAction(byte[] array, byte[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class ByteArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class ByteArrays int to; ByteComparator comp; - MergeSortActionComp(byte[] array, byte[] supp, int from, int to, ByteComparator comp) - { + MergeSortActionComp(byte[] array, byte[] supp, int from, int to, ByteComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class ByteArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class ByteArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + byte[] array; + byte[] supp; + int from; + int to; + ByteComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(byte[] array, byte[] supp, int from, int to, ByteComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; byte[] array; int from; int to; - MemFreeMergeSortAction(byte[] array, int from, int to) - { + MemFreeMergeSortAction(byte[] 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; @@ -1473,8 +1942,7 @@ public class ByteArrays int to; ByteComparator comp; - MemFreeMergeSortActionComp(byte[] array, int from, int to, ByteComparator comp) - { + MemFreeMergeSortActionComp(byte[] array, int from, int to, ByteComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class ByteArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2BooleanLinkedOpenHashMap.java index d47e746..d845412 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2BooleanLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2BooleanLinkedOpenHashMap extends Char2BooleanOpenHashMap imple } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2BooleanLinkedOpenHashMap extends Char2BooleanOpenHashMap imple } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ByteLinkedOpenHashMap.java index e6bfb4e..5eee9b7 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ByteLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2ByteLinkedOpenHashMap extends Char2ByteOpenHashMap implements } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2ByteLinkedOpenHashMap extends Char2ByteOpenHashMap implements } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2CharLinkedOpenHashMap.java index 22b4c97..413ff03 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2CharLinkedOpenHashMap.java @@ -241,7 +241,7 @@ public class Char2CharLinkedOpenHashMap extends Char2CharOpenHashMap implements } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -265,7 +265,7 @@ public class Char2CharLinkedOpenHashMap extends Char2CharOpenHashMap implements } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2DoubleLinkedOpenHashMap.java index 18bc032..5495389 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2DoubleLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2DoubleLinkedOpenHashMap extends Char2DoubleOpenHashMap impleme } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2DoubleLinkedOpenHashMap extends Char2DoubleOpenHashMap impleme } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2FloatLinkedOpenHashMap.java index 7c338d1..281613b 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2FloatLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2FloatLinkedOpenHashMap extends Char2FloatOpenHashMap implement } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2FloatLinkedOpenHashMap extends Char2FloatOpenHashMap implement } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2IntLinkedOpenHashMap.java index 263b54d..24393f5 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2IntLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2IntLinkedOpenHashMap extends Char2IntOpenHashMap implements Ch } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2IntLinkedOpenHashMap extends Char2IntOpenHashMap implements Ch } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2LongLinkedOpenHashMap.java index 63d42f2..2fd35d2 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2LongLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2LongLinkedOpenHashMap extends Char2LongOpenHashMap implements } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2LongLinkedOpenHashMap extends Char2LongOpenHashMap implements } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ObjectLinkedOpenHashMap.java index 5d6d286..8bae512 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ObjectLinkedOpenHashMap.java @@ -243,7 +243,7 @@ public class Char2ObjectLinkedOpenHashMap extends Char2ObjectOpenHashMap i } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -267,7 +267,7 @@ public class Char2ObjectLinkedOpenHashMap extends Char2ObjectOpenHashMap i } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ShortLinkedOpenHashMap.java index 962ba93..5a87b06 100644 --- a/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/chars/maps/impl/hash/Char2ShortLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Char2ShortLinkedOpenHashMap extends Char2ShortOpenHashMap implement } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Char2ShortLinkedOpenHashMap extends Char2ShortOpenHashMap implement } else { int pos = HashUtil.mix(Character.hashCode(key)) & mask; - while(key == (char)0) { + while(keys[pos] != (char)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/chars/utils/CharArrays.java b/src/main/java/speiger/src/collections/chars/utils/CharArrays.java index fe9a2e5..94a5fa0 100644 --- a/src/main/java/speiger/src/collections/chars/utils/CharArrays.java +++ b/src/main/java/speiger/src/collections/chars/utils/CharArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.chars.functions.CharComparator; import speiger.src.collections.chars.collections.CharIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class CharArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static char[] indirectShuffle(char[] 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 + * @return the provided sorted array + */ + public static char[] indirectShuffle(char[] 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 + * @return the provided sorted array + */ + public static char[] indirectShuffle(char[] 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 + * @return the provided sorted array + */ + public static char[] indirectShuffle(char[] 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); + char t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class CharArrays } } + /** + * 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 + * @return input array + */ + public static char[] indirectInsertionSort(char[] array, CharComparator 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 + */ + public static void indirectInsertionSort(char[] array, int length, CharComparator 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 + */ + public static void indirectInsertionSort(char[] array, int from, int to, CharComparator 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 @@ -569,6 +675,57 @@ public class CharArrays } } + /** + * 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 + * @return input array + */ + public static char[] indirectSelectionSort(char[] array, CharComparator 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 + */ + public static void indirectSelectionSort(char[] array, int length, CharComparator 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 + */ + public static void indirectSelectionSort(char[] array, int from, int to, CharComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + char 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); + char 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 @@ -662,6 +819,69 @@ public class CharArrays } } + /** + * 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 + * @return input array + */ + public static char[] indirectMergeSort(char[] array, CharComparator 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 + */ + public static void indirectMergeSort(char[] array, int length, CharComparator 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 + */ + public static void indirectMergeSort(char[] array, char[] supp, int from, int to, CharComparator 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 @@ -752,6 +972,53 @@ public class CharArrays 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 + */ + public static void indirectParallelMergeSort(char[] array, CharComparator 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 + */ + public static void indirectParallelMergeSort(char[] array, int length, CharComparator 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 + */ + public static void indirectParallelMergeSort(char[] array, char[] supp, int from, int to, CharComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class CharArrays 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 + * @return input array + */ + public static char[] indirectQuickSort(char[] array, CharComparator 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 + */ + public static void indirectQuickSort(char[] array, int length, CharComparator 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 + */ + public static void indirectQuickSort(char[] array, int from, int to, CharComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + char 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, @@ -1185,6 +1516,55 @@ public class CharArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(char[] array, CharComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(char[] array, int length, CharComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(char[] array, int from, int to, CharComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class CharArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; char[] array; @@ -1343,8 +1774,7 @@ public class CharArrays int from; int to; - MergeSortAction(char[] array, char[] supp, int from, int to) - { + MergeSortAction(char[] array, char[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class CharArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class CharArrays int to; CharComparator comp; - MergeSortActionComp(char[] array, char[] supp, int from, int to, CharComparator comp) - { + MergeSortActionComp(char[] array, char[] supp, int from, int to, CharComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class CharArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class CharArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + char[] array; + char[] supp; + int from; + int to; + CharComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(char[] array, char[] supp, int from, int to, CharComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; char[] array; int from; int to; - MemFreeMergeSortAction(char[] array, int from, int to) - { + MemFreeMergeSortAction(char[] 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; @@ -1473,8 +1942,7 @@ public class CharArrays int to; CharComparator comp; - MemFreeMergeSortActionComp(char[] array, int from, int to, CharComparator comp) - { + MemFreeMergeSortActionComp(char[] array, int from, int to, CharComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class CharArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2BooleanLinkedOpenHashMap.java index 9a85a4f..e393278 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2BooleanLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2BooleanLinkedOpenHashMap extends Double2BooleanOpenHashMap i } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2BooleanLinkedOpenHashMap extends Double2BooleanOpenHashMap i } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ByteLinkedOpenHashMap.java index e4cbf52..281b5ec 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ByteLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2ByteLinkedOpenHashMap extends Double2ByteOpenHashMap impleme } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2ByteLinkedOpenHashMap extends Double2ByteOpenHashMap impleme } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2CharLinkedOpenHashMap.java index 73c7b9a..e77a5bc 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2CharLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2CharLinkedOpenHashMap extends Double2CharOpenHashMap impleme } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2CharLinkedOpenHashMap extends Double2CharOpenHashMap impleme } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2DoubleLinkedOpenHashMap.java index 131be58..42f57ec 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2DoubleLinkedOpenHashMap.java @@ -241,7 +241,7 @@ public class Double2DoubleLinkedOpenHashMap extends Double2DoubleOpenHashMap imp } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -265,7 +265,7 @@ public class Double2DoubleLinkedOpenHashMap extends Double2DoubleOpenHashMap imp } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2FloatLinkedOpenHashMap.java index 4fa6138..a986f7c 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2FloatLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2FloatLinkedOpenHashMap extends Double2FloatOpenHashMap imple } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2FloatLinkedOpenHashMap extends Double2FloatOpenHashMap imple } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2IntLinkedOpenHashMap.java index 01e5e3e..0dcf234 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2IntLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2IntLinkedOpenHashMap extends Double2IntOpenHashMap implement } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2IntLinkedOpenHashMap extends Double2IntOpenHashMap implement } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2LongLinkedOpenHashMap.java index 25dfaf5..eef2943 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2LongLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2LongLinkedOpenHashMap extends Double2LongOpenHashMap impleme } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2LongLinkedOpenHashMap extends Double2LongOpenHashMap impleme } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ObjectLinkedOpenHashMap.java index 06b6fc7..6e5bd5d 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ObjectLinkedOpenHashMap.java @@ -243,7 +243,7 @@ public class Double2ObjectLinkedOpenHashMap extends Double2ObjectOpenHashMap< } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -267,7 +267,7 @@ public class Double2ObjectLinkedOpenHashMap extends Double2ObjectOpenHashMap< } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ShortLinkedOpenHashMap.java index a636a66..7b254c1 100644 --- a/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/doubles/maps/impl/hash/Double2ShortLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Double2ShortLinkedOpenHashMap extends Double2ShortOpenHashMap imple } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Double2ShortLinkedOpenHashMap extends Double2ShortOpenHashMap imple } else { int pos = HashUtil.mix(Double.hashCode(key)) & mask; - while(Double.doubleToLongBits(key) == 0) { + while(Double.doubleToLongBits(keys[pos]) != 0) { if(Double.doubleToLongBits(keys[pos]) == Double.doubleToLongBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/doubles/utils/DoubleArrays.java b/src/main/java/speiger/src/collections/doubles/utils/DoubleArrays.java index 7e9f1bb..35edfbd 100644 --- a/src/main/java/speiger/src/collections/doubles/utils/DoubleArrays.java +++ b/src/main/java/speiger/src/collections/doubles/utils/DoubleArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.doubles.functions.DoubleComparator; import speiger.src.collections.doubles.collections.DoubleIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class DoubleArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static double[] indirectShuffle(double[] 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 + * @return the provided sorted array + */ + public static double[] indirectShuffle(double[] 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 + * @return the provided sorted array + */ + public static double[] indirectShuffle(double[] 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 + * @return the provided sorted array + */ + public static double[] indirectShuffle(double[] 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); + double t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class DoubleArrays } } + /** + * 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 + * @return input array + */ + public static double[] indirectInsertionSort(double[] array, DoubleComparator 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 + */ + public static void indirectInsertionSort(double[] array, int length, DoubleComparator 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 + */ + public static void indirectInsertionSort(double[] array, int from, int to, DoubleComparator 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 @@ -569,6 +675,57 @@ public class DoubleArrays } } + /** + * 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 + * @return input array + */ + public static double[] indirectSelectionSort(double[] array, DoubleComparator 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 + */ + public static void indirectSelectionSort(double[] array, int length, DoubleComparator 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 + */ + public static void indirectSelectionSort(double[] array, int from, int to, DoubleComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + double 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); + double 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 @@ -662,6 +819,69 @@ public class DoubleArrays } } + /** + * 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 + * @return input array + */ + public static double[] indirectMergeSort(double[] array, DoubleComparator 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 + */ + public static void indirectMergeSort(double[] array, int length, DoubleComparator 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 + */ + public static void indirectMergeSort(double[] array, double[] supp, int from, int to, DoubleComparator 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 @@ -752,6 +972,53 @@ public class DoubleArrays 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 + */ + public static void indirectParallelMergeSort(double[] array, DoubleComparator 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 + */ + public static void indirectParallelMergeSort(double[] array, int length, DoubleComparator 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 + */ + public static void indirectParallelMergeSort(double[] array, double[] supp, int from, int to, DoubleComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class DoubleArrays 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 + * @return input array + */ + public static double[] indirectQuickSort(double[] array, DoubleComparator 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 + */ + public static void indirectQuickSort(double[] array, int length, DoubleComparator 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 + */ + public static void indirectQuickSort(double[] array, int from, int to, DoubleComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + double 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, @@ -1185,6 +1516,55 @@ public class DoubleArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(double[] array, DoubleComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(double[] array, int length, DoubleComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(double[] array, int from, int to, DoubleComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class DoubleArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; double[] array; @@ -1343,8 +1774,7 @@ public class DoubleArrays int from; int to; - MergeSortAction(double[] array, double[] supp, int from, int to) - { + MergeSortAction(double[] array, double[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class DoubleArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class DoubleArrays int to; DoubleComparator comp; - MergeSortActionComp(double[] array, double[] supp, int from, int to, DoubleComparator comp) - { + MergeSortActionComp(double[] array, double[] supp, int from, int to, DoubleComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class DoubleArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class DoubleArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + double[] array; + double[] supp; + int from; + int to; + DoubleComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(double[] array, double[] supp, int from, int to, DoubleComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; double[] array; int from; int to; - MemFreeMergeSortAction(double[] array, int from, int to) - { + MemFreeMergeSortAction(double[] 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; @@ -1473,8 +1942,7 @@ public class DoubleArrays int to; DoubleComparator comp; - MemFreeMergeSortActionComp(double[] array, int from, int to, DoubleComparator comp) - { + MemFreeMergeSortActionComp(double[] array, int from, int to, DoubleComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class DoubleArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2BooleanLinkedOpenHashMap.java index e506012..ba9cf0c 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2BooleanLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2BooleanLinkedOpenHashMap extends Float2BooleanOpenHashMap imp } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2BooleanLinkedOpenHashMap extends Float2BooleanOpenHashMap imp } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ByteLinkedOpenHashMap.java index 86022f9..544c031 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ByteLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2ByteLinkedOpenHashMap extends Float2ByteOpenHashMap implement } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2ByteLinkedOpenHashMap extends Float2ByteOpenHashMap implement } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2CharLinkedOpenHashMap.java index 93c06da..4e37254 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2CharLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2CharLinkedOpenHashMap extends Float2CharOpenHashMap implement } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2CharLinkedOpenHashMap extends Float2CharOpenHashMap implement } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2DoubleLinkedOpenHashMap.java index b8d81e7..f5cdbfc 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2DoubleLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2DoubleLinkedOpenHashMap extends Float2DoubleOpenHashMap imple } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2DoubleLinkedOpenHashMap extends Float2DoubleOpenHashMap imple } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2FloatLinkedOpenHashMap.java index 6271004..16742c2 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2FloatLinkedOpenHashMap.java @@ -241,7 +241,7 @@ public class Float2FloatLinkedOpenHashMap extends Float2FloatOpenHashMap impleme } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -265,7 +265,7 @@ public class Float2FloatLinkedOpenHashMap extends Float2FloatOpenHashMap impleme } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2IntLinkedOpenHashMap.java index f796185..b322be6 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2IntLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2IntLinkedOpenHashMap extends Float2IntOpenHashMap implements } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2IntLinkedOpenHashMap extends Float2IntOpenHashMap implements } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2LongLinkedOpenHashMap.java index a7e0ac5..7173eb4 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2LongLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2LongLinkedOpenHashMap extends Float2LongOpenHashMap implement } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2LongLinkedOpenHashMap extends Float2LongOpenHashMap implement } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ObjectLinkedOpenHashMap.java index 353d912..048f8a9 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ObjectLinkedOpenHashMap.java @@ -243,7 +243,7 @@ public class Float2ObjectLinkedOpenHashMap extends Float2ObjectOpenHashMap } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -267,7 +267,7 @@ public class Float2ObjectLinkedOpenHashMap extends Float2ObjectOpenHashMap } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ShortLinkedOpenHashMap.java index 1f52c73..bce8795 100644 --- a/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/floats/maps/impl/hash/Float2ShortLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Float2ShortLinkedOpenHashMap extends Float2ShortOpenHashMap impleme } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Float2ShortLinkedOpenHashMap extends Float2ShortOpenHashMap impleme } else { int pos = HashUtil.mix(Float.hashCode(key)) & mask; - while(Float.floatToIntBits(key) == 0) { + while(Float.floatToIntBits(keys[pos]) != 0) { if(Float.floatToIntBits(keys[pos]) == Float.floatToIntBits(key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/floats/utils/FloatArrays.java b/src/main/java/speiger/src/collections/floats/utils/FloatArrays.java index 8010dee..13af827 100644 --- a/src/main/java/speiger/src/collections/floats/utils/FloatArrays.java +++ b/src/main/java/speiger/src/collections/floats/utils/FloatArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.floats.functions.FloatComparator; import speiger.src.collections.floats.collections.FloatIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class FloatArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static float[] indirectShuffle(float[] 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 + * @return the provided sorted array + */ + public static float[] indirectShuffle(float[] 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 + * @return the provided sorted array + */ + public static float[] indirectShuffle(float[] 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 + * @return the provided sorted array + */ + public static float[] indirectShuffle(float[] 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); + float t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class FloatArrays } } + /** + * 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 + * @return input array + */ + public static float[] indirectInsertionSort(float[] array, FloatComparator 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 + */ + public static void indirectInsertionSort(float[] array, int length, FloatComparator 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 + */ + public static void indirectInsertionSort(float[] array, int from, int to, FloatComparator 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 @@ -569,6 +675,57 @@ public class FloatArrays } } + /** + * 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 + * @return input array + */ + public static float[] indirectSelectionSort(float[] array, FloatComparator 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 + */ + public static void indirectSelectionSort(float[] array, int length, FloatComparator 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 + */ + public static void indirectSelectionSort(float[] array, int from, int to, FloatComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + float 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); + float 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 @@ -662,6 +819,69 @@ public class FloatArrays } } + /** + * 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 + * @return input array + */ + public static float[] indirectMergeSort(float[] array, FloatComparator 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 + */ + public static void indirectMergeSort(float[] array, int length, FloatComparator 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 + */ + public static void indirectMergeSort(float[] array, float[] supp, int from, int to, FloatComparator 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 @@ -752,6 +972,53 @@ public class FloatArrays 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 + */ + public static void indirectParallelMergeSort(float[] array, FloatComparator 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 + */ + public static void indirectParallelMergeSort(float[] array, int length, FloatComparator 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 + */ + public static void indirectParallelMergeSort(float[] array, float[] supp, int from, int to, FloatComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class FloatArrays 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 + * @return input array + */ + public static float[] indirectQuickSort(float[] array, FloatComparator 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 + */ + public static void indirectQuickSort(float[] array, int length, FloatComparator 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 + */ + public static void indirectQuickSort(float[] array, int from, int to, FloatComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + float 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, @@ -1185,6 +1516,55 @@ public class FloatArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(float[] array, FloatComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(float[] array, int length, FloatComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(float[] array, int from, int to, FloatComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class FloatArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; float[] array; @@ -1343,8 +1774,7 @@ public class FloatArrays int from; int to; - MergeSortAction(float[] array, float[] supp, int from, int to) - { + MergeSortAction(float[] array, float[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class FloatArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class FloatArrays int to; FloatComparator comp; - MergeSortActionComp(float[] array, float[] supp, int from, int to, FloatComparator comp) - { + MergeSortActionComp(float[] array, float[] supp, int from, int to, FloatComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class FloatArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class FloatArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + float[] array; + float[] supp; + int from; + int to; + FloatComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(float[] array, float[] supp, int from, int to, FloatComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; float[] array; int from; int to; - MemFreeMergeSortAction(float[] array, int from, int to) - { + MemFreeMergeSortAction(float[] 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; @@ -1473,8 +1942,7 @@ public class FloatArrays int to; FloatComparator comp; - MemFreeMergeSortActionComp(float[] array, int from, int to, FloatComparator comp) - { + MemFreeMergeSortActionComp(float[] array, int from, int to, FloatComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class FloatArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2BooleanLinkedOpenHashMap.java index fe01f95..6205b87 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2BooleanLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2BooleanLinkedOpenHashMap extends Int2BooleanOpenHashMap impleme } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2BooleanLinkedOpenHashMap extends Int2BooleanOpenHashMap impleme } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ByteLinkedOpenHashMap.java index ff242f9..b876d82 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ByteLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2ByteLinkedOpenHashMap extends Int2ByteOpenHashMap implements In } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2ByteLinkedOpenHashMap extends Int2ByteOpenHashMap implements In } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2CharLinkedOpenHashMap.java index 96fdf0e..29ff0e2 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2CharLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2CharLinkedOpenHashMap extends Int2CharOpenHashMap implements In } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2CharLinkedOpenHashMap extends Int2CharOpenHashMap implements In } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2DoubleLinkedOpenHashMap.java index 26cd007..387eec3 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2DoubleLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2DoubleLinkedOpenHashMap extends Int2DoubleOpenHashMap implement } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2DoubleLinkedOpenHashMap extends Int2DoubleOpenHashMap implement } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2FloatLinkedOpenHashMap.java index 4b254a3..74e593a 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2FloatLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2FloatLinkedOpenHashMap extends Int2FloatOpenHashMap implements } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2FloatLinkedOpenHashMap extends Int2FloatOpenHashMap implements } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2IntLinkedOpenHashMap.java index b3bbd10..3d49a44 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2IntLinkedOpenHashMap.java @@ -240,7 +240,7 @@ public class Int2IntLinkedOpenHashMap extends Int2IntOpenHashMap implements Int2 } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -264,7 +264,7 @@ public class Int2IntLinkedOpenHashMap extends Int2IntOpenHashMap implements Int2 } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2LongLinkedOpenHashMap.java index b966e9f..33008d7 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2LongLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2LongLinkedOpenHashMap extends Int2LongOpenHashMap implements In } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2LongLinkedOpenHashMap extends Int2LongOpenHashMap implements In } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ObjectLinkedOpenHashMap.java index 23f77d1..42063b5 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ObjectLinkedOpenHashMap.java @@ -242,7 +242,7 @@ public class Int2ObjectLinkedOpenHashMap extends Int2ObjectOpenHashMap imp } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -266,7 +266,7 @@ public class Int2ObjectLinkedOpenHashMap extends Int2ObjectOpenHashMap imp } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ShortLinkedOpenHashMap.java index 9bbd306..08d7dab 100644 --- a/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/ints/maps/impl/hash/Int2ShortLinkedOpenHashMap.java @@ -248,7 +248,7 @@ public class Int2ShortLinkedOpenHashMap extends Int2ShortOpenHashMap implements } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -272,7 +272,7 @@ public class Int2ShortLinkedOpenHashMap extends Int2ShortOpenHashMap implements } else { int pos = HashUtil.mix(Integer.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/ints/utils/IntArrays.java b/src/main/java/speiger/src/collections/ints/utils/IntArrays.java index 9256d2f..ad15266 100644 --- a/src/main/java/speiger/src/collections/ints/utils/IntArrays.java +++ b/src/main/java/speiger/src/collections/ints/utils/IntArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.ints.functions.IntComparator; import speiger.src.collections.ints.collections.IntIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class IntArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static int[] indirectShuffle(int[] 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 + * @return the provided sorted array + */ + public static int[] indirectShuffle(int[] 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 + * @return the provided sorted array + */ + public static int[] indirectShuffle(int[] 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 + * @return the provided sorted array + */ + public static int[] indirectShuffle(int[] 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); + int t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class IntArrays } } + /** + * 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 + * @return input array + */ + public static int[] indirectInsertionSort(int[] array, IntComparator 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 + */ + public static void indirectInsertionSort(int[] array, int length, IntComparator 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 + */ + public static void indirectInsertionSort(int[] array, int from, int to, IntComparator 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 @@ -569,6 +675,57 @@ public class IntArrays } } + /** + * 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 + * @return input array + */ + public static int[] indirectSelectionSort(int[] array, IntComparator 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 + */ + public static void indirectSelectionSort(int[] array, int length, IntComparator 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 + */ + public static void indirectSelectionSort(int[] array, int from, int to, IntComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + int 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); + int 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 @@ -662,6 +819,69 @@ public class IntArrays } } + /** + * 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 + * @return input array + */ + public static int[] indirectMergeSort(int[] array, IntComparator 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 + */ + public static void indirectMergeSort(int[] array, int length, IntComparator 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 + */ + public static void indirectMergeSort(int[] array, int[] supp, int from, int to, IntComparator 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 @@ -752,6 +972,53 @@ public class IntArrays 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 + */ + public static void indirectParallelMergeSort(int[] array, IntComparator 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 + */ + public static void indirectParallelMergeSort(int[] array, int length, IntComparator 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 + */ + public static void indirectParallelMergeSort(int[] array, int[] supp, int from, int to, IntComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class IntArrays 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 + * @return input array + */ + public static int[] indirectQuickSort(int[] array, IntComparator 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 + */ + public static void indirectQuickSort(int[] array, int length, IntComparator 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 + */ + public static void indirectQuickSort(int[] array, int from, int to, IntComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + int 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, @@ -1185,6 +1516,55 @@ public class IntArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(int[] array, IntComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(int[] array, int length, IntComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(int[] array, int from, int to, IntComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class IntArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; int[] array; @@ -1343,8 +1774,7 @@ public class IntArrays int from; int to; - MergeSortAction(int[] array, int[] supp, int from, int to) - { + MergeSortAction(int[] array, int[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class IntArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class IntArrays int to; IntComparator comp; - MergeSortActionComp(int[] array, int[] supp, int from, int to, IntComparator comp) - { + MergeSortActionComp(int[] array, int[] supp, int from, int to, IntComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class IntArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class IntArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + int[] array; + int[] supp; + int from; + int to; + IntComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(int[] array, int[] supp, int from, int to, IntComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; int[] array; int from; int to; - MemFreeMergeSortAction(int[] array, int from, int to) - { + MemFreeMergeSortAction(int[] 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; @@ -1473,8 +1942,7 @@ public class IntArrays int to; IntComparator comp; - MemFreeMergeSortActionComp(int[] array, int from, int to, IntComparator comp) - { + MemFreeMergeSortActionComp(int[] array, int from, int to, IntComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class IntArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2BooleanLinkedOpenHashMap.java index 7e47e1e..b9363f4 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2BooleanLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2BooleanLinkedOpenHashMap extends Long2BooleanOpenHashMap imple } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2BooleanLinkedOpenHashMap extends Long2BooleanOpenHashMap imple } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ByteLinkedOpenHashMap.java index 3850561..2ed0177 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ByteLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2ByteLinkedOpenHashMap extends Long2ByteOpenHashMap implements } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2ByteLinkedOpenHashMap extends Long2ByteOpenHashMap implements } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2CharLinkedOpenHashMap.java index 7c8dfbd..95867de 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2CharLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2CharLinkedOpenHashMap extends Long2CharOpenHashMap implements } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2CharLinkedOpenHashMap extends Long2CharOpenHashMap implements } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2DoubleLinkedOpenHashMap.java index af77db6..c21e396 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2DoubleLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2DoubleLinkedOpenHashMap extends Long2DoubleOpenHashMap impleme } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2DoubleLinkedOpenHashMap extends Long2DoubleOpenHashMap impleme } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2FloatLinkedOpenHashMap.java index 7640bf0..170eac6 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2FloatLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2FloatLinkedOpenHashMap extends Long2FloatOpenHashMap implement } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2FloatLinkedOpenHashMap extends Long2FloatOpenHashMap implement } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2IntLinkedOpenHashMap.java index 73c72c3..bcca589 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2IntLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2IntLinkedOpenHashMap extends Long2IntOpenHashMap implements Lo } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2IntLinkedOpenHashMap extends Long2IntOpenHashMap implements Lo } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2LongLinkedOpenHashMap.java index 2304f9b..d27b484 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2LongLinkedOpenHashMap.java @@ -241,7 +241,7 @@ public class Long2LongLinkedOpenHashMap extends Long2LongOpenHashMap implements } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -265,7 +265,7 @@ public class Long2LongLinkedOpenHashMap extends Long2LongOpenHashMap implements } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ObjectLinkedOpenHashMap.java index 4fbbc12..f4cb732 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ObjectLinkedOpenHashMap.java @@ -243,7 +243,7 @@ public class Long2ObjectLinkedOpenHashMap extends Long2ObjectOpenHashMap i } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -267,7 +267,7 @@ public class Long2ObjectLinkedOpenHashMap extends Long2ObjectOpenHashMap i } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ShortLinkedOpenHashMap.java index 94d39e1..12a9c2b 100644 --- a/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/longs/maps/impl/hash/Long2ShortLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Long2ShortLinkedOpenHashMap extends Long2ShortOpenHashMap implement } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Long2ShortLinkedOpenHashMap extends Long2ShortOpenHashMap implement } else { int pos = HashUtil.mix(Long.hashCode(key)) & mask; - while(key == 0) { + while(keys[pos] != 0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/longs/utils/LongArrays.java b/src/main/java/speiger/src/collections/longs/utils/LongArrays.java index a6fc830..253ec4c 100644 --- a/src/main/java/speiger/src/collections/longs/utils/LongArrays.java +++ b/src/main/java/speiger/src/collections/longs/utils/LongArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.longs.functions.LongComparator; import speiger.src.collections.longs.collections.LongIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class LongArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static long[] indirectShuffle(long[] 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 + * @return the provided sorted array + */ + public static long[] indirectShuffle(long[] 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 + * @return the provided sorted array + */ + public static long[] indirectShuffle(long[] 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 + * @return the provided sorted array + */ + public static long[] indirectShuffle(long[] 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); + long t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class LongArrays } } + /** + * 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 + * @return input array + */ + public static long[] indirectInsertionSort(long[] array, LongComparator 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 + */ + public static void indirectInsertionSort(long[] array, int length, LongComparator 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 + */ + public static void indirectInsertionSort(long[] array, int from, int to, LongComparator 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 @@ -569,6 +675,57 @@ public class LongArrays } } + /** + * 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 + * @return input array + */ + public static long[] indirectSelectionSort(long[] array, LongComparator 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 + */ + public static void indirectSelectionSort(long[] array, int length, LongComparator 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 + */ + public static void indirectSelectionSort(long[] array, int from, int to, LongComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + long 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); + long 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 @@ -662,6 +819,69 @@ public class LongArrays } } + /** + * 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 + * @return input array + */ + public static long[] indirectMergeSort(long[] array, LongComparator 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 + */ + public static void indirectMergeSort(long[] array, int length, LongComparator 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 + */ + public static void indirectMergeSort(long[] array, long[] supp, int from, int to, LongComparator 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 @@ -752,6 +972,53 @@ public class LongArrays 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 + */ + public static void indirectParallelMergeSort(long[] array, LongComparator 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 + */ + public static void indirectParallelMergeSort(long[] array, int length, LongComparator 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 + */ + public static void indirectParallelMergeSort(long[] array, long[] supp, int from, int to, LongComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class LongArrays 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 + * @return input array + */ + public static long[] indirectQuickSort(long[] array, LongComparator 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 + */ + public static void indirectQuickSort(long[] array, int length, LongComparator 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 + */ + public static void indirectQuickSort(long[] array, int from, int to, LongComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + long 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, @@ -1185,6 +1516,55 @@ public class LongArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(long[] array, LongComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(long[] array, int length, LongComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(long[] array, int from, int to, LongComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class LongArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; long[] array; @@ -1343,8 +1774,7 @@ public class LongArrays int from; int to; - MergeSortAction(long[] array, long[] supp, int from, int to) - { + MergeSortAction(long[] array, long[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class LongArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class LongArrays int to; LongComparator comp; - MergeSortActionComp(long[] array, long[] supp, int from, int to, LongComparator comp) - { + MergeSortActionComp(long[] array, long[] supp, int from, int to, LongComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class LongArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class LongArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + long[] array; + long[] supp; + int from; + int to; + LongComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(long[] array, long[] supp, int from, int to, LongComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; long[] array; int from; int to; - MemFreeMergeSortAction(long[] array, int from, int to) - { + MemFreeMergeSortAction(long[] 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; @@ -1473,8 +1942,7 @@ public class LongArrays int to; LongComparator comp; - MemFreeMergeSortActionComp(long[] array, int from, int to, LongComparator comp) - { + MemFreeMergeSortActionComp(long[] array, int from, int to, LongComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class LongArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2BooleanLinkedOpenHashMap.java index 22774db..7048362 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2BooleanLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2BooleanLinkedOpenHashMap extends Object2BooleanOpenHashMa } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2BooleanLinkedOpenHashMap extends Object2BooleanOpenHashMa } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ByteLinkedOpenHashMap.java index fc82a5f..a17bba5 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ByteLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2ByteLinkedOpenHashMap extends Object2ByteOpenHashMap i } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2ByteLinkedOpenHashMap extends Object2ByteOpenHashMap i } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2CharLinkedOpenHashMap.java index 1e48337..6206096 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2CharLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2CharLinkedOpenHashMap extends Object2CharOpenHashMap i } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2CharLinkedOpenHashMap extends Object2CharOpenHashMap i } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2DoubleLinkedOpenHashMap.java index 31d2e71..efae849 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2DoubleLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2DoubleLinkedOpenHashMap extends Object2DoubleOpenHashMap< } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2DoubleLinkedOpenHashMap extends Object2DoubleOpenHashMap< } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2FloatLinkedOpenHashMap.java index ff5f220..4b0e0eb 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2FloatLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2FloatLinkedOpenHashMap extends Object2FloatOpenHashMap } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2FloatLinkedOpenHashMap extends Object2FloatOpenHashMap } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2IntLinkedOpenHashMap.java index 776fa16..055df89 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2IntLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2IntLinkedOpenHashMap extends Object2IntOpenHashMap imp } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2IntLinkedOpenHashMap extends Object2IntOpenHashMap imp } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2LongLinkedOpenHashMap.java index 2f357df..39f740e 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2LongLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2LongLinkedOpenHashMap extends Object2LongOpenHashMap i } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2LongLinkedOpenHashMap extends Object2LongOpenHashMap i } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ObjectLinkedOpenHashMap.java index cf35067..e09e97f 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ObjectLinkedOpenHashMap.java @@ -208,7 +208,7 @@ public class Object2ObjectLinkedOpenHashMap extends Object2ObjectOpenHashM } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -232,7 +232,7 @@ public class Object2ObjectLinkedOpenHashMap extends Object2ObjectOpenHashM } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ShortLinkedOpenHashMap.java index 9e4dff1..044d4b1 100644 --- a/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/objects/maps/impl/hash/Object2ShortLinkedOpenHashMap.java @@ -239,7 +239,7 @@ public class Object2ShortLinkedOpenHashMap extends Object2ShortOpenHashMap } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } @@ -263,7 +263,7 @@ public class Object2ShortLinkedOpenHashMap extends Object2ShortOpenHashMap } else { int pos = HashUtil.mix(Objects.hashCode(key)) & mask; - while(key == null) { + while(keys[pos] != null) { if(Objects.equals(keys[pos], key)) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanHashMap.java new file mode 100644 index 0000000..11e679a --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanHashMap.java @@ -0,0 +1,1350 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntBooleanConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectBooleanConsumer; +import speiger.src.collections.objects.functions.function.ObjectBooleanUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2BooleanMap; +import speiger.src.collections.objects.maps.interfaces.Object2BooleanMap; +import speiger.src.collections.booleans.collections.AbstractBooleanCollection; +import speiger.src.collections.booleans.collections.BooleanCollection; +import speiger.src.collections.booleans.functions.BooleanSupplier; +import speiger.src.collections.booleans.functions.function.BooleanBooleanUnaryOperator; + +import speiger.src.collections.booleans.collections.BooleanIterator; +import speiger.src.collections.booleans.functions.BooleanConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.booleans.functions.function.BooleanPredicate; +import speiger.src.collections.booleans.functions.OptionalBoolean; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2BooleanHashMap extends AbstractObject2BooleanMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient boolean[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient BooleanCollection 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 Reference2BooleanHashMap() { + 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 Reference2BooleanHashMap(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 Reference2BooleanHashMap(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 boolean[this.capacity]; + } + + /** + * 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 Reference2BooleanHashMap(T[] keys, Boolean[] 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 Reference2BooleanHashMap(T[] keys, Boolean[] 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 Reference2BooleanHashMap(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 Reference2BooleanHashMap(Object2BooleanMap 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 Reference2BooleanHashMap(Object2BooleanMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public boolean put(T key, boolean value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + boolean oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public boolean putIfAbsent(T key, boolean value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(values[slot] == getDefaultReturnValue()) { + boolean oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(boolean value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && values[i] == value) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Boolean.valueOf(values[i])))) return true; + return false; + } + + @Override + public boolean rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public boolean remOrDefault(T key, boolean defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Boolean remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Boolean.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, boolean value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Boolean.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Boolean.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public boolean getBoolean(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Boolean get(Object key) { + int slot = findIndex(key); + return Boolean.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public boolean getOrDefault(T key, boolean defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2BooleanHashMap copy() { + Reference2BooleanHashMap map = new Reference2BooleanHashMap<>(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> object2BooleanEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public BooleanCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectBooleanConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, boolean oldValue, boolean newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public boolean replace(T key, boolean value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + boolean oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean computeBoolean(T key, ObjectBooleanUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + boolean newValue = mappingFunction.applyAsBoolean(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + boolean newValue = mappingFunction.applyAsBoolean(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public boolean computeBooleanIfAbsent(T key, Predicate mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + boolean newValue = mappingFunction.test(key); + insert(-index-1, key, newValue); + return newValue; + } + boolean newValue = values[index]; + return newValue; + } + + @Override + public boolean supplyBooleanIfAbsent(T key, BooleanSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + boolean newValue = valueProvider.getAsBoolean(); + insert(-index-1, key, newValue); + return newValue; + } + boolean newValue = values[index]; + return newValue; + } + + @Override + public boolean computeBooleanIfPresent(T key, ObjectBooleanUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + boolean newValue = mappingFunction.applyAsBoolean(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public boolean computeBooleanNonDefault(T key, ObjectBooleanUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + boolean newValue = mappingFunction.applyAsBoolean(key, getDefaultReturnValue()); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + boolean newValue = mappingFunction.applyAsBoolean(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public boolean computeBooleanIfAbsentNonDefault(T key, Predicate mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + boolean newValue = mappingFunction.test(key); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + boolean newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = mappingFunction.test(key); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public boolean supplyBooleanIfAbsentNonDefault(T key, BooleanSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + boolean newValue = valueProvider.getAsBoolean(); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + boolean newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = valueProvider.getAsBoolean(); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public boolean computeBooleanIfPresentNonDefault(T key, ObjectBooleanUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || values[index] == getDefaultReturnValue()) return getDefaultReturnValue(); + boolean newValue = mappingFunction.applyAsBoolean(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public boolean mergeBoolean(T key, boolean value, BooleanBooleanUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + boolean newValue = index < 0 || values[index] == getDefaultReturnValue() ? value : mappingFunction.applyAsBoolean(values[index], value); + if(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 mergeAllBoolean(Object2BooleanMap m, BooleanBooleanUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2BooleanMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + boolean newValue = index < 0 || values[index] == getDefaultReturnValue() ? entry.getBooleanValue() : mappingFunction.applyAsBoolean(values[index], entry.getBooleanValue()); + if(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected boolean removeIndex(int pos) { + boolean value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = false; + 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, T key, boolean 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]; + boolean[] newValues = new boolean[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = false; + 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 T key; + protected boolean value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public boolean getBooleanValue() { + return value; + } + + @Override + public boolean setValue(boolean value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2BooleanMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public boolean getBooleanValue() { + return values[index]; + } + + @Override + public boolean setValue(boolean value) { + boolean oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2BooleanMap.Entry) { + Object2BooleanMap.Entry entry = (Object2BooleanMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && getBooleanValue() == entry.getBooleanValue(); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Boolean && Objects.equals(getKey(), key) && getBooleanValue() == ((Boolean)value).booleanValue(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Boolean.hashCode(getBooleanValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Boolean.toString(getBooleanValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2BooleanMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2BooleanMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2BooleanMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2BooleanHashMap.this.size(); + } + + @Override + public void clear() { + Reference2BooleanHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2BooleanMap.Entry) { + Object2BooleanMap.Entry entry = (Object2BooleanMap.Entry)o; + int index = Reference2BooleanHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getBooleanValue() == Reference2BooleanHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2BooleanHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Boolean.valueOf(Reference2BooleanHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2BooleanMap.Entry) { + Object2BooleanMap.Entry entry = (Object2BooleanMap.Entry)o; + return Reference2BooleanHashMap.this.remove(entry.getKey(), entry.getBooleanValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2BooleanHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2BooleanHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2BooleanHashMap.this.size(); + } + + @Override + public void clear() { + Reference2BooleanHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractBooleanCollection { + @Override + public boolean contains(boolean e) { + return containsValue(e); + } + + @Override + public boolean add(boolean o) { + throw new UnsupportedOperationException(); + } + + @Override + public BooleanIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2BooleanHashMap.this.size(); + } + + @Override + public void clear() { + Reference2BooleanHashMap.this.clear(); + } + + @Override + public void forEach(BooleanConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntBooleanConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectBooleanConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(BooleanPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(BooleanPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(BooleanPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean reduce(boolean identity, BooleanBooleanUnaryOperator operator) { + Objects.requireNonNull(operator); + boolean state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsBoolean(state, values[i]); + } + return state; + } + + @Override + public OptionalBoolean reduce(BooleanBooleanUnaryOperator operator) { + Objects.requireNonNull(operator); + boolean state = false; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsBoolean(state, values[i]); + } + return empty ? OptionalBoolean.empty() : OptionalBoolean.of(state); + } + + @Override + public OptionalBoolean findFirst(BooleanPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalBoolean.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalBoolean.of(values[i]); + } + return OptionalBoolean.empty(); + } + + @Override + public int count(BooleanPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2BooleanMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2BooleanMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements BooleanIterator { + @Override + public boolean nextBoolean() { + 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(keys[pos] != null){ + 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 { + Reference2BooleanHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = false; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanLinkedHashMap.java new file mode 100644 index 0000000..279c3cc --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2BooleanLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntBooleanConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectBooleanConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2BooleanMap; +import speiger.src.collections.objects.maps.interfaces.Object2BooleanOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.booleans.collections.AbstractBooleanCollection; +import speiger.src.collections.booleans.collections.BooleanOrderedCollection; +import speiger.src.collections.booleans.collections.BooleanIterator; +import speiger.src.collections.booleans.functions.function.BooleanBooleanUnaryOperator; +import speiger.src.collections.booleans.functions.BooleanConsumer; +import speiger.src.collections.booleans.lists.BooleanListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.booleans.functions.function.BooleanPredicate; +import speiger.src.collections.booleans.functions.OptionalBoolean; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2BooleanLinkedHashMap extends Reference2BooleanHashMap implements Object2BooleanOrderedMap +{ + /** 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 Reference2BooleanLinkedHashMap() { + 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 Reference2BooleanLinkedHashMap(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 Reference2BooleanLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2BooleanLinkedHashMap(T[] keys, Boolean[] 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 Reference2BooleanLinkedHashMap(T[] keys, Boolean[] 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 Reference2BooleanLinkedHashMap(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 Reference2BooleanLinkedHashMap(Object2BooleanMap 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 Reference2BooleanLinkedHashMap(Object2BooleanMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public boolean putAndMoveToFirst(T key, boolean value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + boolean 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 boolean putAndMoveToLast(T key, boolean value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + boolean 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 boolean putFirst(T key, boolean value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 boolean putLast(T key, boolean value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public boolean getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(boolean value) { + int index = firstIndex; + while(index != -1) { + if(values[index] == value) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Boolean.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2BooleanLinkedHashMap copy() { + Reference2BooleanLinkedHashMap map = new Reference2BooleanLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 boolean firstBooleanValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public boolean lastBooleanValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2BooleanMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2BooleanMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2BooleanMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2BooleanMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2BooleanEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public BooleanOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (BooleanOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectBooleanConsumer 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]; + boolean[] newValues = new boolean[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2BooleanOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2BooleanMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2BooleanMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2BooleanMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2BooleanMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2BooleanMap.Entry o) { + return Reference2BooleanLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2BooleanMap.Entry o) { + return Reference2BooleanLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2BooleanMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstBooleanValue()); + } + + @Override + public Object2BooleanMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastBooleanValue()); + } + + @Override + public Object2BooleanMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstBooleanValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2BooleanMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastBooleanValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2BooleanMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2BooleanMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2BooleanMap.Entry 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 Object2BooleanMap.Entry) { + Object2BooleanMap.Entry entry = (Object2BooleanMap.Entry)o; + int index = Reference2BooleanLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getBooleanValue() == Reference2BooleanLinkedHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2BooleanLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Boolean.valueOf(Reference2BooleanLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2BooleanMap.Entry) { + Object2BooleanMap.Entry entry = (Object2BooleanMap.Entry)o; + return Reference2BooleanLinkedHashMap.this.remove(entry.getKey(), entry.getBooleanValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2BooleanLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2BooleanLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2BooleanLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2BooleanLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2BooleanLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2BooleanLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2BooleanLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2BooleanLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractBooleanCollection implements BooleanOrderedCollection { + @Override + public boolean contains(boolean e) { return containsValue(e); } + + @Override + public boolean add(boolean o) { throw new UnsupportedOperationException(); } + @Override + public BooleanIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2BooleanLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2BooleanLinkedHashMap.this.clear(); } + @Override + public BooleanOrderedCollection reversed() { return new AbstractBooleanCollection.ReverseBooleanOrderedCollection(this, this::reverseIterator); } + private BooleanIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(boolean e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(boolean e) { throw new UnsupportedOperationException(); } + @Override + public boolean getFirstBoolean() { return firstBooleanValue(); } + @Override + public boolean removeFirstBoolean() { + boolean result = firstBooleanValue(); + pollFirstKey(); + return result; + } + @Override + public boolean getLastBoolean() { return lastBooleanValue(); } + @Override + public boolean removeLastBoolean() { + boolean result = lastBooleanValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(BooleanConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntBooleanConsumer 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, ObjectBooleanConsumer 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(BooleanPredicate 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(BooleanPredicate 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(BooleanPredicate 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 reduce(boolean identity, BooleanBooleanUnaryOperator operator) { + Objects.requireNonNull(operator); + boolean state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsBoolean(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalBoolean reduce(BooleanBooleanUnaryOperator operator) { + Objects.requireNonNull(operator); + boolean state = false; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsBoolean(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalBoolean.empty() : OptionalBoolean.of(state); + } + + @Override + public OptionalBoolean findFirst(BooleanPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalBoolean.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalBoolean.of(values[index]); + index = (int)links[index]; + } + return OptionalBoolean.empty(); + } + + @Override + public int count(BooleanPredicate 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(T from) { + super(from); + } + + @Override + public Object2BooleanMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2BooleanMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2BooleanMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2BooleanMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2BooleanMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2BooleanMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2BooleanMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2BooleanMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements BooleanListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public boolean previousBoolean() { + return values[previousEntry()]; + } + + @Override + public boolean nextBoolean() { + return values[nextEntry()]; + } + + @Override + public void set(boolean e) { throw new UnsupportedOperationException(); } + + @Override + public void add(boolean 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = false; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = false; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteHashMap.java new file mode 100644 index 0000000..5dadc1b --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntByteConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectByteConsumer; +import speiger.src.collections.objects.functions.function.ToByteFunction; +import speiger.src.collections.objects.functions.function.ObjectByteUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2ByteMap; +import speiger.src.collections.objects.maps.interfaces.Object2ByteMap; +import speiger.src.collections.bytes.collections.AbstractByteCollection; +import speiger.src.collections.bytes.collections.ByteCollection; +import speiger.src.collections.bytes.functions.ByteSupplier; +import speiger.src.collections.bytes.functions.function.ByteByteUnaryOperator; + +import speiger.src.collections.bytes.collections.ByteIterator; +import speiger.src.collections.bytes.functions.ByteConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.bytes.functions.function.BytePredicate; +import speiger.src.collections.bytes.functions.OptionalByte; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2ByteHashMap extends AbstractObject2ByteMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient byte[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient ByteCollection 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 Reference2ByteHashMap() { + 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 Reference2ByteHashMap(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 Reference2ByteHashMap(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 byte[this.capacity]; + } + + /** + * 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 Reference2ByteHashMap(T[] keys, Byte[] 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 Reference2ByteHashMap(T[] keys, Byte[] 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 Reference2ByteHashMap(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 Reference2ByteHashMap(Object2ByteMap 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 Reference2ByteHashMap(Object2ByteMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public byte put(T key, byte value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + byte oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public byte putIfAbsent(T key, byte value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(values[slot] == getDefaultReturnValue()) { + byte oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public byte addTo(T key, byte value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + byte oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public byte subFrom(T key, byte value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + byte oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(byte value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && values[i] == value) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Byte.valueOf(values[i])))) return true; + return false; + } + + @Override + public byte rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public byte remOrDefault(T key, byte defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Byte remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Byte.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, byte value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Byte.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Byte.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public byte getByte(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Byte get(Object key) { + int slot = findIndex(key); + return Byte.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public byte getOrDefault(T key, byte defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2ByteHashMap copy() { + Reference2ByteHashMap map = new Reference2ByteHashMap<>(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> object2ByteEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public ByteCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectByteConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, byte oldValue, byte newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public byte replace(T key, byte value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + byte oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public byte computeByte(T key, ObjectByteUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + byte newValue = mappingFunction.applyAsByte(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + byte newValue = mappingFunction.applyAsByte(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public byte computeByteIfAbsent(T key, ToByteFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + byte newValue = mappingFunction.applyAsByte(key); + insert(-index-1, key, newValue); + return newValue; + } + byte newValue = values[index]; + return newValue; + } + + @Override + public byte supplyByteIfAbsent(T key, ByteSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + byte newValue = valueProvider.getAsByte(); + insert(-index-1, key, newValue); + return newValue; + } + byte newValue = values[index]; + return newValue; + } + + @Override + public byte computeByteIfPresent(T key, ObjectByteUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + byte newValue = mappingFunction.applyAsByte(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public byte computeByteNonDefault(T key, ObjectByteUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + byte newValue = mappingFunction.applyAsByte(key, getDefaultReturnValue()); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + byte newValue = mappingFunction.applyAsByte(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public byte computeByteIfAbsentNonDefault(T key, ToByteFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + byte newValue = mappingFunction.applyAsByte(key); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + byte newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = mappingFunction.applyAsByte(key); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public byte supplyByteIfAbsentNonDefault(T key, ByteSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + byte newValue = valueProvider.getAsByte(); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + byte newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = valueProvider.getAsByte(); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public byte computeByteIfPresentNonDefault(T key, ObjectByteUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || values[index] == getDefaultReturnValue()) return getDefaultReturnValue(); + byte newValue = mappingFunction.applyAsByte(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public byte mergeByte(T key, byte value, ByteByteUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + byte newValue = index < 0 || values[index] == getDefaultReturnValue() ? value : mappingFunction.applyAsByte(values[index], value); + if(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 mergeAllByte(Object2ByteMap m, ByteByteUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2ByteMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + byte newValue = index < 0 || values[index] == getDefaultReturnValue() ? entry.getByteValue() : mappingFunction.applyAsByte(values[index], entry.getByteValue()); + if(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected byte removeIndex(int pos) { + byte value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = (byte)0; + 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, T key, byte 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]; + byte[] newValues = new byte[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (byte)0; + 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 T key; + protected byte value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public byte getByteValue() { + return value; + } + + @Override + public byte setValue(byte value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2ByteMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public byte getByteValue() { + return values[index]; + } + + @Override + public byte setValue(byte value) { + byte oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2ByteMap.Entry) { + Object2ByteMap.Entry entry = (Object2ByteMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && getByteValue() == entry.getByteValue(); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Byte && Objects.equals(getKey(), key) && getByteValue() == ((Byte)value).byteValue(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Byte.hashCode(getByteValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Byte.toString(getByteValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2ByteMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2ByteMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2ByteMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2ByteHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ByteHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ByteMap.Entry) { + Object2ByteMap.Entry entry = (Object2ByteMap.Entry)o; + int index = Reference2ByteHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getByteValue() == Reference2ByteHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2ByteHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Byte.valueOf(Reference2ByteHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ByteMap.Entry) { + Object2ByteMap.Entry entry = (Object2ByteMap.Entry)o; + return Reference2ByteHashMap.this.remove(entry.getKey(), entry.getByteValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2ByteHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2ByteHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2ByteHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ByteHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractByteCollection { + @Override + public boolean contains(byte e) { + return containsValue(e); + } + + @Override + public boolean add(byte o) { + throw new UnsupportedOperationException(); + } + + @Override + public ByteIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2ByteHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ByteHashMap.this.clear(); + } + + @Override + public void forEach(ByteConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntByteConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectByteConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(BytePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(BytePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(BytePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public byte reduce(byte identity, ByteByteUnaryOperator operator) { + Objects.requireNonNull(operator); + byte state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsByte(state, values[i]); + } + return state; + } + + @Override + public OptionalByte reduce(ByteByteUnaryOperator operator) { + Objects.requireNonNull(operator); + byte state = (byte)0; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsByte(state, values[i]); + } + return empty ? OptionalByte.empty() : OptionalByte.of(state); + } + + @Override + public OptionalByte findFirst(BytePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalByte.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalByte.of(values[i]); + } + return OptionalByte.empty(); + } + + @Override + public int count(BytePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2ByteMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2ByteMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements ByteIterator { + @Override + public byte nextByte() { + 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(keys[pos] != null){ + 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 { + Reference2ByteHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (byte)0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteLinkedHashMap.java new file mode 100644 index 0000000..0e20000 --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ByteLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntByteConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectByteConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2ByteMap; +import speiger.src.collections.objects.maps.interfaces.Object2ByteOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.bytes.collections.AbstractByteCollection; +import speiger.src.collections.bytes.collections.ByteOrderedCollection; +import speiger.src.collections.bytes.collections.ByteIterator; +import speiger.src.collections.bytes.functions.function.ByteByteUnaryOperator; +import speiger.src.collections.bytes.functions.ByteConsumer; +import speiger.src.collections.bytes.lists.ByteListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.bytes.functions.function.BytePredicate; +import speiger.src.collections.bytes.functions.OptionalByte; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2ByteLinkedHashMap extends Reference2ByteHashMap implements Object2ByteOrderedMap +{ + /** 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 Reference2ByteLinkedHashMap() { + 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 Reference2ByteLinkedHashMap(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 Reference2ByteLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2ByteLinkedHashMap(T[] keys, Byte[] 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 Reference2ByteLinkedHashMap(T[] keys, Byte[] 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 Reference2ByteLinkedHashMap(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 Reference2ByteLinkedHashMap(Object2ByteMap 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 Reference2ByteLinkedHashMap(Object2ByteMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public byte putAndMoveToFirst(T key, byte value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + byte 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 byte putAndMoveToLast(T key, byte value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + byte 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 byte putFirst(T key, byte value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 byte putLast(T key, byte value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public byte getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public byte getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(byte value) { + int index = firstIndex; + while(index != -1) { + if(values[index] == value) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Byte.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2ByteLinkedHashMap copy() { + Reference2ByteLinkedHashMap map = new Reference2ByteLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 byte firstByteValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public byte lastByteValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2ByteMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2ByteMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2ByteMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2ByteMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2ByteEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public ByteOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (ByteOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectByteConsumer 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]; + byte[] newValues = new byte[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2ByteOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2ByteMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2ByteMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2ByteMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2ByteMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2ByteMap.Entry o) { + return Reference2ByteLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2ByteMap.Entry o) { + return Reference2ByteLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2ByteMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstByteValue()); + } + + @Override + public Object2ByteMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastByteValue()); + } + + @Override + public Object2ByteMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstByteValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2ByteMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastByteValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2ByteMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2ByteMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2ByteMap.Entry 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 Object2ByteMap.Entry) { + Object2ByteMap.Entry entry = (Object2ByteMap.Entry)o; + int index = Reference2ByteLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getByteValue() == Reference2ByteLinkedHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2ByteLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Byte.valueOf(Reference2ByteLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ByteMap.Entry) { + Object2ByteMap.Entry entry = (Object2ByteMap.Entry)o; + return Reference2ByteLinkedHashMap.this.remove(entry.getKey(), entry.getByteValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2ByteLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2ByteLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ByteLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2ByteLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2ByteLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2ByteLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2ByteLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ByteLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractByteCollection implements ByteOrderedCollection { + @Override + public boolean contains(byte e) { return containsValue(e); } + + @Override + public boolean add(byte o) { throw new UnsupportedOperationException(); } + @Override + public ByteIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2ByteLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2ByteLinkedHashMap.this.clear(); } + @Override + public ByteOrderedCollection reversed() { return new AbstractByteCollection.ReverseByteOrderedCollection(this, this::reverseIterator); } + private ByteIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(byte e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(byte e) { throw new UnsupportedOperationException(); } + @Override + public byte getFirstByte() { return firstByteValue(); } + @Override + public byte removeFirstByte() { + byte result = firstByteValue(); + pollFirstKey(); + return result; + } + @Override + public byte getLastByte() { return lastByteValue(); } + @Override + public byte removeLastByte() { + byte result = lastByteValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(ByteConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntByteConsumer 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, ObjectByteConsumer 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(BytePredicate 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(BytePredicate 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(BytePredicate 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 byte reduce(byte identity, ByteByteUnaryOperator operator) { + Objects.requireNonNull(operator); + byte state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsByte(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalByte reduce(ByteByteUnaryOperator operator) { + Objects.requireNonNull(operator); + byte state = (byte)0; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsByte(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalByte.empty() : OptionalByte.of(state); + } + + @Override + public OptionalByte findFirst(BytePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalByte.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalByte.of(values[index]); + index = (int)links[index]; + } + return OptionalByte.empty(); + } + + @Override + public int count(BytePredicate 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(T from) { + super(from); + } + + @Override + public Object2ByteMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2ByteMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2ByteMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2ByteMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2ByteMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2ByteMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2ByteMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2ByteMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements ByteListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public byte previousByte() { + return values[previousEntry()]; + } + + @Override + public byte nextByte() { + return values[nextEntry()]; + } + + @Override + public void set(byte e) { throw new UnsupportedOperationException(); } + + @Override + public void add(byte 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = (byte)0; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (byte)0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharHashMap.java new file mode 100644 index 0000000..5858e7a --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntCharConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectCharConsumer; +import speiger.src.collections.objects.functions.function.ToCharFunction; +import speiger.src.collections.objects.functions.function.ObjectCharUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2CharMap; +import speiger.src.collections.objects.maps.interfaces.Object2CharMap; +import speiger.src.collections.chars.collections.AbstractCharCollection; +import speiger.src.collections.chars.collections.CharCollection; +import speiger.src.collections.chars.functions.CharSupplier; +import speiger.src.collections.chars.functions.function.CharCharUnaryOperator; + +import speiger.src.collections.chars.collections.CharIterator; +import speiger.src.collections.chars.functions.CharConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.chars.functions.function.CharPredicate; +import speiger.src.collections.chars.functions.OptionalChar; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2CharHashMap extends AbstractObject2CharMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient char[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient CharCollection 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 Reference2CharHashMap() { + 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 Reference2CharHashMap(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 Reference2CharHashMap(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 char[this.capacity]; + } + + /** + * 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 Reference2CharHashMap(T[] keys, Character[] 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 Reference2CharHashMap(T[] keys, Character[] 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 Reference2CharHashMap(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 Reference2CharHashMap(Object2CharMap 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 Reference2CharHashMap(Object2CharMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public char put(T key, char value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + char oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public char putIfAbsent(T key, char value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(values[slot] == getDefaultReturnValue()) { + char oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public char addTo(T key, char value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + char oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public char subFrom(T key, char value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + char oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(char value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && values[i] == value) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Character.valueOf(values[i])))) return true; + return false; + } + + @Override + public char rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public char remOrDefault(T key, char defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Character remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Character.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, char value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Character.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Character.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public char getChar(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Character get(Object key) { + int slot = findIndex(key); + return Character.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public char getOrDefault(T key, char defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2CharHashMap copy() { + Reference2CharHashMap map = new Reference2CharHashMap<>(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> object2CharEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public CharCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectCharConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, char oldValue, char newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public char replace(T key, char value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + char oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public char computeChar(T key, ObjectCharUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + char newValue = mappingFunction.applyAsChar(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + char newValue = mappingFunction.applyAsChar(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public char computeCharIfAbsent(T key, ToCharFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + char newValue = mappingFunction.applyAsChar(key); + insert(-index-1, key, newValue); + return newValue; + } + char newValue = values[index]; + return newValue; + } + + @Override + public char supplyCharIfAbsent(T key, CharSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + char newValue = valueProvider.getAsChar(); + insert(-index-1, key, newValue); + return newValue; + } + char newValue = values[index]; + return newValue; + } + + @Override + public char computeCharIfPresent(T key, ObjectCharUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + char newValue = mappingFunction.applyAsChar(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public char computeCharNonDefault(T key, ObjectCharUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + char newValue = mappingFunction.applyAsChar(key, getDefaultReturnValue()); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + char newValue = mappingFunction.applyAsChar(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public char computeCharIfAbsentNonDefault(T key, ToCharFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + char newValue = mappingFunction.applyAsChar(key); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + char newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = mappingFunction.applyAsChar(key); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public char supplyCharIfAbsentNonDefault(T key, CharSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + char newValue = valueProvider.getAsChar(); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + char newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = valueProvider.getAsChar(); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public char computeCharIfPresentNonDefault(T key, ObjectCharUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || values[index] == getDefaultReturnValue()) return getDefaultReturnValue(); + char newValue = mappingFunction.applyAsChar(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public char mergeChar(T key, char value, CharCharUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + char newValue = index < 0 || values[index] == getDefaultReturnValue() ? value : mappingFunction.applyAsChar(values[index], value); + if(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 mergeAllChar(Object2CharMap m, CharCharUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2CharMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + char newValue = index < 0 || values[index] == getDefaultReturnValue() ? entry.getCharValue() : mappingFunction.applyAsChar(values[index], entry.getCharValue()); + if(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected char removeIndex(int pos) { + char value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = (char)0; + 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, T key, char 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]; + char[] newValues = new char[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (char)0; + 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 T key; + protected char value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public char getCharValue() { + return value; + } + + @Override + public char setValue(char value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2CharMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public char getCharValue() { + return values[index]; + } + + @Override + public char setValue(char value) { + char oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2CharMap.Entry) { + Object2CharMap.Entry entry = (Object2CharMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && getCharValue() == entry.getCharValue(); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Character && Objects.equals(getKey(), key) && getCharValue() == ((Character)value).charValue(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Character.hashCode(getCharValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Character.toString(getCharValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2CharMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2CharMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2CharMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2CharHashMap.this.size(); + } + + @Override + public void clear() { + Reference2CharHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2CharMap.Entry) { + Object2CharMap.Entry entry = (Object2CharMap.Entry)o; + int index = Reference2CharHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getCharValue() == Reference2CharHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2CharHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Character.valueOf(Reference2CharHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2CharMap.Entry) { + Object2CharMap.Entry entry = (Object2CharMap.Entry)o; + return Reference2CharHashMap.this.remove(entry.getKey(), entry.getCharValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2CharHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2CharHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2CharHashMap.this.size(); + } + + @Override + public void clear() { + Reference2CharHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractCharCollection { + @Override + public boolean contains(char e) { + return containsValue(e); + } + + @Override + public boolean add(char o) { + throw new UnsupportedOperationException(); + } + + @Override + public CharIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2CharHashMap.this.size(); + } + + @Override + public void clear() { + Reference2CharHashMap.this.clear(); + } + + @Override + public void forEach(CharConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntCharConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectCharConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(CharPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(CharPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(CharPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public char reduce(char identity, CharCharUnaryOperator operator) { + Objects.requireNonNull(operator); + char state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsChar(state, values[i]); + } + return state; + } + + @Override + public OptionalChar reduce(CharCharUnaryOperator operator) { + Objects.requireNonNull(operator); + char state = (char)0; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsChar(state, values[i]); + } + return empty ? OptionalChar.empty() : OptionalChar.of(state); + } + + @Override + public OptionalChar findFirst(CharPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalChar.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalChar.of(values[i]); + } + return OptionalChar.empty(); + } + + @Override + public int count(CharPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2CharMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2CharMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements CharIterator { + @Override + public char nextChar() { + 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(keys[pos] != null){ + 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 { + Reference2CharHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (char)0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharLinkedHashMap.java new file mode 100644 index 0000000..3f19ac8 --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2CharLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntCharConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectCharConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2CharMap; +import speiger.src.collections.objects.maps.interfaces.Object2CharOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.chars.collections.AbstractCharCollection; +import speiger.src.collections.chars.collections.CharOrderedCollection; +import speiger.src.collections.chars.collections.CharIterator; +import speiger.src.collections.chars.functions.function.CharCharUnaryOperator; +import speiger.src.collections.chars.functions.CharConsumer; +import speiger.src.collections.chars.lists.CharListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.chars.functions.function.CharPredicate; +import speiger.src.collections.chars.functions.OptionalChar; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2CharLinkedHashMap extends Reference2CharHashMap implements Object2CharOrderedMap +{ + /** 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 Reference2CharLinkedHashMap() { + 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 Reference2CharLinkedHashMap(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 Reference2CharLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2CharLinkedHashMap(T[] keys, Character[] 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 Reference2CharLinkedHashMap(T[] keys, Character[] 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 Reference2CharLinkedHashMap(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 Reference2CharLinkedHashMap(Object2CharMap 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 Reference2CharLinkedHashMap(Object2CharMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public char putAndMoveToFirst(T key, char value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + char 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 char putAndMoveToLast(T key, char value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + char 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 char putFirst(T key, char value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 char putLast(T key, char value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public char getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public char getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(char value) { + int index = firstIndex; + while(index != -1) { + if(values[index] == value) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Character.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2CharLinkedHashMap copy() { + Reference2CharLinkedHashMap map = new Reference2CharLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 char firstCharValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public char lastCharValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2CharMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2CharMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2CharMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2CharMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2CharEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public CharOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (CharOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectCharConsumer 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]; + char[] newValues = new char[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2CharOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2CharMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2CharMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2CharMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2CharMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2CharMap.Entry o) { + return Reference2CharLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2CharMap.Entry o) { + return Reference2CharLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2CharMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstCharValue()); + } + + @Override + public Object2CharMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastCharValue()); + } + + @Override + public Object2CharMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstCharValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2CharMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastCharValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2CharMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2CharMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2CharMap.Entry 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 Object2CharMap.Entry) { + Object2CharMap.Entry entry = (Object2CharMap.Entry)o; + int index = Reference2CharLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getCharValue() == Reference2CharLinkedHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2CharLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Character.valueOf(Reference2CharLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2CharMap.Entry) { + Object2CharMap.Entry entry = (Object2CharMap.Entry)o; + return Reference2CharLinkedHashMap.this.remove(entry.getKey(), entry.getCharValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2CharLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2CharLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2CharLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2CharLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2CharLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2CharLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2CharLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2CharLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractCharCollection implements CharOrderedCollection { + @Override + public boolean contains(char e) { return containsValue(e); } + + @Override + public boolean add(char o) { throw new UnsupportedOperationException(); } + @Override + public CharIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2CharLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2CharLinkedHashMap.this.clear(); } + @Override + public CharOrderedCollection reversed() { return new AbstractCharCollection.ReverseCharOrderedCollection(this, this::reverseIterator); } + private CharIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(char e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(char e) { throw new UnsupportedOperationException(); } + @Override + public char getFirstChar() { return firstCharValue(); } + @Override + public char removeFirstChar() { + char result = firstCharValue(); + pollFirstKey(); + return result; + } + @Override + public char getLastChar() { return lastCharValue(); } + @Override + public char removeLastChar() { + char result = lastCharValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(CharConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntCharConsumer 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, ObjectCharConsumer 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(CharPredicate 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(CharPredicate 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(CharPredicate 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 char reduce(char identity, CharCharUnaryOperator operator) { + Objects.requireNonNull(operator); + char state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsChar(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalChar reduce(CharCharUnaryOperator operator) { + Objects.requireNonNull(operator); + char state = (char)0; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsChar(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalChar.empty() : OptionalChar.of(state); + } + + @Override + public OptionalChar findFirst(CharPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalChar.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalChar.of(values[index]); + index = (int)links[index]; + } + return OptionalChar.empty(); + } + + @Override + public int count(CharPredicate 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(T from) { + super(from); + } + + @Override + public Object2CharMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2CharMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2CharMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2CharMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2CharMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2CharMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2CharMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2CharMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements CharListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public char previousChar() { + return values[previousEntry()]; + } + + @Override + public char nextChar() { + return values[nextEntry()]; + } + + @Override + public void set(char e) { throw new UnsupportedOperationException(); } + + @Override + public void add(char 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = (char)0; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (char)0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleHashMap.java new file mode 100644 index 0000000..4e4164a --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; +import java.util.function.DoublePredicate; +import java.util.OptionalDouble; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntDoubleConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectDoubleConsumer; +import speiger.src.collections.objects.functions.function.ToDoubleFunction; +import speiger.src.collections.objects.functions.function.ObjectDoubleUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2DoubleMap; +import speiger.src.collections.objects.maps.interfaces.Object2DoubleMap; +import speiger.src.collections.doubles.collections.AbstractDoubleCollection; +import speiger.src.collections.doubles.collections.DoubleCollection; +import speiger.src.collections.doubles.functions.DoubleSupplier; +import speiger.src.collections.doubles.functions.function.DoubleDoubleUnaryOperator; + +import speiger.src.collections.doubles.collections.DoubleIterator; +import speiger.src.collections.doubles.functions.DoubleConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2DoubleHashMap extends AbstractObject2DoubleMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient double[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient DoubleCollection 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 Reference2DoubleHashMap() { + 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 Reference2DoubleHashMap(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 Reference2DoubleHashMap(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 double[this.capacity]; + } + + /** + * 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 Reference2DoubleHashMap(T[] keys, Double[] 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 Reference2DoubleHashMap(T[] keys, Double[] 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 Reference2DoubleHashMap(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 Reference2DoubleHashMap(Object2DoubleMap 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 Reference2DoubleHashMap(Object2DoubleMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public double put(T key, double value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + double oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public double putIfAbsent(T key, double value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(Double.doubleToLongBits(values[slot]) == Double.doubleToLongBits(getDefaultReturnValue())) { + double oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public double addTo(T key, double value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + double oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public double subFrom(T key, double value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + double oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(double value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && Double.doubleToLongBits(values[i]) == Double.doubleToLongBits(value)) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Double.valueOf(values[i])))) return true; + return false; + } + + @Override + public double rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public double remOrDefault(T key, double defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Double remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Double.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, double value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && Double.doubleToLongBits(value) == Double.doubleToLongBits(values[pos])) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && Double.doubleToLongBits(value) == Double.doubleToLongBits(values[pos])) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Double.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Double.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public double getDouble(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Double get(Object key) { + int slot = findIndex(key); + return Double.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public double getOrDefault(T key, double defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2DoubleHashMap copy() { + Reference2DoubleHashMap map = new Reference2DoubleHashMap<>(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> object2DoubleEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public DoubleCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectDoubleConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, double oldValue, double newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public double replace(T key, double value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + double oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public double computeDouble(T key, ObjectDoubleUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + double newValue = mappingFunction.applyAsDouble(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + double newValue = mappingFunction.applyAsDouble(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public double computeDoubleIfAbsent(T key, ToDoubleFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + double newValue = mappingFunction.applyAsDouble(key); + insert(-index-1, key, newValue); + return newValue; + } + double newValue = values[index]; + return newValue; + } + + @Override + public double supplyDoubleIfAbsent(T key, DoubleSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + double newValue = valueProvider.getAsDouble(); + insert(-index-1, key, newValue); + return newValue; + } + double newValue = values[index]; + return newValue; + } + + @Override + public double computeDoubleIfPresent(T key, ObjectDoubleUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + double newValue = mappingFunction.applyAsDouble(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public double computeDoubleNonDefault(T key, ObjectDoubleUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + double newValue = mappingFunction.applyAsDouble(key, getDefaultReturnValue()); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + double newValue = mappingFunction.applyAsDouble(key, values[index]); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public double computeDoubleIfAbsentNonDefault(T key, ToDoubleFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + double newValue = mappingFunction.applyAsDouble(key); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + double newValue = values[index]; + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) { + newValue = mappingFunction.applyAsDouble(key); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public double supplyDoubleIfAbsentNonDefault(T key, DoubleSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + double newValue = valueProvider.getAsDouble(); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + double newValue = values[index]; + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) { + newValue = valueProvider.getAsDouble(); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public double computeDoubleIfPresentNonDefault(T key, ObjectDoubleUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || Double.doubleToLongBits(values[index]) == Double.doubleToLongBits(getDefaultReturnValue())) return getDefaultReturnValue(); + double newValue = mappingFunction.applyAsDouble(key, values[index]); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public double mergeDouble(T key, double value, DoubleDoubleUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + double newValue = index < 0 || Double.doubleToLongBits(values[index]) == Double.doubleToLongBits(getDefaultReturnValue()) ? value : mappingFunction.applyAsDouble(values[index], value); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(getDefaultReturnValue())) { + if(index >= 0) + removeIndex(index); + } + else if(index < 0) insert(-index-1, key, newValue); + else values[index] = newValue; + return newValue; + } + + @Override + public void mergeAllDouble(Object2DoubleMap m, DoubleDoubleUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2DoubleMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + double newValue = index < 0 || Double.doubleToLongBits(values[index]) == Double.doubleToLongBits(getDefaultReturnValue()) ? entry.getDoubleValue() : mappingFunction.applyAsDouble(values[index], entry.getDoubleValue()); + if(Double.doubleToLongBits(newValue) == Double.doubleToLongBits(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected double removeIndex(int pos) { + double value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = 0D; + 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, T key, double 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]; + double[] newValues = new double[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0D; + 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 T key; + protected double value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public double getDoubleValue() { + return value; + } + + @Override + public double setValue(double value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2DoubleMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public double getDoubleValue() { + return values[index]; + } + + @Override + public double setValue(double value) { + double oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2DoubleMap.Entry) { + Object2DoubleMap.Entry entry = (Object2DoubleMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && Double.doubleToLongBits(getDoubleValue()) == Double.doubleToLongBits(entry.getDoubleValue()); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Double && Objects.equals(getKey(), key) && Double.doubleToLongBits(getDoubleValue()) == Double.doubleToLongBits(((Double)value).doubleValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Double.hashCode(getDoubleValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Double.toString(getDoubleValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2DoubleMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2DoubleMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2DoubleMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2DoubleHashMap.this.size(); + } + + @Override + public void clear() { + Reference2DoubleHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2DoubleMap.Entry) { + Object2DoubleMap.Entry entry = (Object2DoubleMap.Entry)o; + int index = Reference2DoubleHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Double.doubleToLongBits(entry.getDoubleValue()) == Double.doubleToLongBits(Reference2DoubleHashMap.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2DoubleHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Double.valueOf(Reference2DoubleHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2DoubleMap.Entry) { + Object2DoubleMap.Entry entry = (Object2DoubleMap.Entry)o; + return Reference2DoubleHashMap.this.remove(entry.getKey(), entry.getDoubleValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2DoubleHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2DoubleHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2DoubleHashMap.this.size(); + } + + @Override + public void clear() { + Reference2DoubleHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractDoubleCollection { + @Override + public boolean contains(double e) { + return containsValue(e); + } + + @Override + public boolean add(double o) { + throw new UnsupportedOperationException(); + } + + @Override + public DoubleIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2DoubleHashMap.this.size(); + } + + @Override + public void clear() { + Reference2DoubleHashMap.this.clear(); + } + + @Override + public void forEach(DoubleConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntDoubleConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectDoubleConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(DoublePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(DoublePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(DoublePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public double reduce(double identity, DoubleDoubleUnaryOperator operator) { + Objects.requireNonNull(operator); + double state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsDouble(state, values[i]); + } + return state; + } + + @Override + public OptionalDouble reduce(DoubleDoubleUnaryOperator operator) { + Objects.requireNonNull(operator); + double state = 0D; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsDouble(state, values[i]); + } + return empty ? OptionalDouble.empty() : OptionalDouble.of(state); + } + + @Override + public OptionalDouble findFirst(DoublePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalDouble.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalDouble.of(values[i]); + } + return OptionalDouble.empty(); + } + + @Override + public int count(DoublePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2DoubleMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2DoubleMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements DoubleIterator { + @Override + public double nextDouble() { + 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(keys[pos] != null){ + 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 { + Reference2DoubleHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0D; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleLinkedHashMap.java new file mode 100644 index 0000000..38108bd --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2DoubleLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; +import java.util.function.DoublePredicate; +import java.util.OptionalDouble; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntDoubleConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectDoubleConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2DoubleMap; +import speiger.src.collections.objects.maps.interfaces.Object2DoubleOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.doubles.collections.AbstractDoubleCollection; +import speiger.src.collections.doubles.collections.DoubleOrderedCollection; +import speiger.src.collections.doubles.collections.DoubleIterator; +import speiger.src.collections.doubles.functions.function.DoubleDoubleUnaryOperator; +import speiger.src.collections.doubles.functions.DoubleConsumer; +import speiger.src.collections.doubles.lists.DoubleListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2DoubleLinkedHashMap extends Reference2DoubleHashMap implements Object2DoubleOrderedMap +{ + /** 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 Reference2DoubleLinkedHashMap() { + 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 Reference2DoubleLinkedHashMap(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 Reference2DoubleLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2DoubleLinkedHashMap(T[] keys, Double[] 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 Reference2DoubleLinkedHashMap(T[] keys, Double[] 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 Reference2DoubleLinkedHashMap(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 Reference2DoubleLinkedHashMap(Object2DoubleMap 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 Reference2DoubleLinkedHashMap(Object2DoubleMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public double putAndMoveToFirst(T key, double value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + double 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 double putAndMoveToLast(T key, double value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + double 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 double putFirst(T key, double value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 double putLast(T key, double value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public double getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public double getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(double value) { + int index = firstIndex; + while(index != -1) { + if(Double.doubleToLongBits(values[index]) == Double.doubleToLongBits(value)) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Double.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2DoubleLinkedHashMap copy() { + Reference2DoubleLinkedHashMap map = new Reference2DoubleLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 double firstDoubleValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public double lastDoubleValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2DoubleMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2DoubleMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2DoubleMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2DoubleMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2DoubleEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public DoubleOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (DoubleOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectDoubleConsumer 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]; + double[] newValues = new double[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2DoubleOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2DoubleMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2DoubleMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2DoubleMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2DoubleMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2DoubleMap.Entry o) { + return Reference2DoubleLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2DoubleMap.Entry o) { + return Reference2DoubleLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2DoubleMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstDoubleValue()); + } + + @Override + public Object2DoubleMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastDoubleValue()); + } + + @Override + public Object2DoubleMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstDoubleValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2DoubleMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastDoubleValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2DoubleMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2DoubleMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2DoubleMap.Entry 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 Object2DoubleMap.Entry) { + Object2DoubleMap.Entry entry = (Object2DoubleMap.Entry)o; + int index = Reference2DoubleLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Double.doubleToLongBits(entry.getDoubleValue()) == Double.doubleToLongBits(Reference2DoubleLinkedHashMap.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2DoubleLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Double.valueOf(Reference2DoubleLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2DoubleMap.Entry) { + Object2DoubleMap.Entry entry = (Object2DoubleMap.Entry)o; + return Reference2DoubleLinkedHashMap.this.remove(entry.getKey(), entry.getDoubleValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2DoubleLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2DoubleLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2DoubleLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2DoubleLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2DoubleLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2DoubleLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2DoubleLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2DoubleLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractDoubleCollection implements DoubleOrderedCollection { + @Override + public boolean contains(double e) { return containsValue(e); } + + @Override + public boolean add(double o) { throw new UnsupportedOperationException(); } + @Override + public DoubleIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2DoubleLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2DoubleLinkedHashMap.this.clear(); } + @Override + public DoubleOrderedCollection reversed() { return new AbstractDoubleCollection.ReverseDoubleOrderedCollection(this, this::reverseIterator); } + private DoubleIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(double e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(double e) { throw new UnsupportedOperationException(); } + @Override + public double getFirstDouble() { return firstDoubleValue(); } + @Override + public double removeFirstDouble() { + double result = firstDoubleValue(); + pollFirstKey(); + return result; + } + @Override + public double getLastDouble() { return lastDoubleValue(); } + @Override + public double removeLastDouble() { + double result = lastDoubleValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(DoubleConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntDoubleConsumer 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, ObjectDoubleConsumer 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(DoublePredicate 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(DoublePredicate 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(DoublePredicate 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 double reduce(double identity, DoubleDoubleUnaryOperator operator) { + Objects.requireNonNull(operator); + double state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsDouble(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalDouble reduce(DoubleDoubleUnaryOperator operator) { + Objects.requireNonNull(operator); + double state = 0D; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsDouble(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalDouble.empty() : OptionalDouble.of(state); + } + + @Override + public OptionalDouble findFirst(DoublePredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalDouble.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalDouble.of(values[index]); + index = (int)links[index]; + } + return OptionalDouble.empty(); + } + + @Override + public int count(DoublePredicate 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(T from) { + super(from); + } + + @Override + public Object2DoubleMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2DoubleMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2DoubleMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2DoubleMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2DoubleMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2DoubleMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2DoubleMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2DoubleMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements DoubleListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public double previousDouble() { + return values[previousEntry()]; + } + + @Override + public double nextDouble() { + return values[nextEntry()]; + } + + @Override + public void set(double e) { throw new UnsupportedOperationException(); } + + @Override + public void add(double 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = 0D; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0D; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatHashMap.java new file mode 100644 index 0000000..8406926 --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntFloatConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectFloatConsumer; +import speiger.src.collections.objects.functions.function.ToFloatFunction; +import speiger.src.collections.objects.functions.function.ObjectFloatUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2FloatMap; +import speiger.src.collections.objects.maps.interfaces.Object2FloatMap; +import speiger.src.collections.floats.collections.AbstractFloatCollection; +import speiger.src.collections.floats.collections.FloatCollection; +import speiger.src.collections.floats.functions.FloatSupplier; +import speiger.src.collections.floats.functions.function.FloatFloatUnaryOperator; + +import speiger.src.collections.floats.collections.FloatIterator; +import speiger.src.collections.floats.functions.FloatConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.floats.functions.function.FloatPredicate; +import speiger.src.collections.floats.functions.OptionalFloat; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2FloatHashMap extends AbstractObject2FloatMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient float[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient FloatCollection 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 Reference2FloatHashMap() { + 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 Reference2FloatHashMap(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 Reference2FloatHashMap(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 float[this.capacity]; + } + + /** + * 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 Reference2FloatHashMap(T[] keys, Float[] 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 Reference2FloatHashMap(T[] keys, Float[] 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 Reference2FloatHashMap(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 Reference2FloatHashMap(Object2FloatMap 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 Reference2FloatHashMap(Object2FloatMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public float put(T key, float value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + float oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public float putIfAbsent(T key, float value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(Float.floatToIntBits(values[slot]) == Float.floatToIntBits(getDefaultReturnValue())) { + float oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public float addTo(T key, float value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + float oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public float subFrom(T key, float value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + float oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(float value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && Float.floatToIntBits(values[i]) == Float.floatToIntBits(value)) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Float.valueOf(values[i])))) return true; + return false; + } + + @Override + public float rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public float remOrDefault(T key, float defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Float remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Float.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, float value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && Float.floatToIntBits(value) == Float.floatToIntBits(values[pos])) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && Float.floatToIntBits(value) == Float.floatToIntBits(values[pos])) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Float.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Float.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public float getFloat(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Float get(Object key) { + int slot = findIndex(key); + return Float.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public float getOrDefault(T key, float defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2FloatHashMap copy() { + Reference2FloatHashMap map = new Reference2FloatHashMap<>(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> object2FloatEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public FloatCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectFloatConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, float oldValue, float newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public float replace(T key, float value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + float oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public float computeFloat(T key, ObjectFloatUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + float newValue = mappingFunction.applyAsFloat(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + float newValue = mappingFunction.applyAsFloat(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public float computeFloatIfAbsent(T key, ToFloatFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + float newValue = mappingFunction.applyAsFloat(key); + insert(-index-1, key, newValue); + return newValue; + } + float newValue = values[index]; + return newValue; + } + + @Override + public float supplyFloatIfAbsent(T key, FloatSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + float newValue = valueProvider.getAsFloat(); + insert(-index-1, key, newValue); + return newValue; + } + float newValue = values[index]; + return newValue; + } + + @Override + public float computeFloatIfPresent(T key, ObjectFloatUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + float newValue = mappingFunction.applyAsFloat(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public float computeFloatNonDefault(T key, ObjectFloatUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + float newValue = mappingFunction.applyAsFloat(key, getDefaultReturnValue()); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + float newValue = mappingFunction.applyAsFloat(key, values[index]); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public float computeFloatIfAbsentNonDefault(T key, ToFloatFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + float newValue = mappingFunction.applyAsFloat(key); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + float newValue = values[index]; + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) { + newValue = mappingFunction.applyAsFloat(key); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public float supplyFloatIfAbsentNonDefault(T key, FloatSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + float newValue = valueProvider.getAsFloat(); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + float newValue = values[index]; + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) { + newValue = valueProvider.getAsFloat(); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public float computeFloatIfPresentNonDefault(T key, ObjectFloatUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || Float.floatToIntBits(values[index]) == Float.floatToIntBits(getDefaultReturnValue())) return getDefaultReturnValue(); + float newValue = mappingFunction.applyAsFloat(key, values[index]); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public float mergeFloat(T key, float value, FloatFloatUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + float newValue = index < 0 || Float.floatToIntBits(values[index]) == Float.floatToIntBits(getDefaultReturnValue()) ? value : mappingFunction.applyAsFloat(values[index], value); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(getDefaultReturnValue())) { + if(index >= 0) + removeIndex(index); + } + else if(index < 0) insert(-index-1, key, newValue); + else values[index] = newValue; + return newValue; + } + + @Override + public void mergeAllFloat(Object2FloatMap m, FloatFloatUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2FloatMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + float newValue = index < 0 || Float.floatToIntBits(values[index]) == Float.floatToIntBits(getDefaultReturnValue()) ? entry.getFloatValue() : mappingFunction.applyAsFloat(values[index], entry.getFloatValue()); + if(Float.floatToIntBits(newValue) == Float.floatToIntBits(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected float removeIndex(int pos) { + float value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = 0F; + 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, T key, float 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]; + float[] newValues = new float[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0F; + 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 T key; + protected float value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public float getFloatValue() { + return value; + } + + @Override + public float setValue(float value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2FloatMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public float getFloatValue() { + return values[index]; + } + + @Override + public float setValue(float value) { + float oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2FloatMap.Entry) { + Object2FloatMap.Entry entry = (Object2FloatMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && Float.floatToIntBits(getFloatValue()) == Float.floatToIntBits(entry.getFloatValue()); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Float && Objects.equals(getKey(), key) && Float.floatToIntBits(getFloatValue()) == Float.floatToIntBits(((Float)value).floatValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Float.hashCode(getFloatValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Float.toString(getFloatValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2FloatMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2FloatMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2FloatMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2FloatHashMap.this.size(); + } + + @Override + public void clear() { + Reference2FloatHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2FloatMap.Entry) { + Object2FloatMap.Entry entry = (Object2FloatMap.Entry)o; + int index = Reference2FloatHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Float.floatToIntBits(entry.getFloatValue()) == Float.floatToIntBits(Reference2FloatHashMap.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2FloatHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Float.valueOf(Reference2FloatHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2FloatMap.Entry) { + Object2FloatMap.Entry entry = (Object2FloatMap.Entry)o; + return Reference2FloatHashMap.this.remove(entry.getKey(), entry.getFloatValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2FloatHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2FloatHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2FloatHashMap.this.size(); + } + + @Override + public void clear() { + Reference2FloatHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractFloatCollection { + @Override + public boolean contains(float e) { + return containsValue(e); + } + + @Override + public boolean add(float o) { + throw new UnsupportedOperationException(); + } + + @Override + public FloatIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2FloatHashMap.this.size(); + } + + @Override + public void clear() { + Reference2FloatHashMap.this.clear(); + } + + @Override + public void forEach(FloatConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntFloatConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectFloatConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(FloatPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(FloatPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(FloatPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public float reduce(float identity, FloatFloatUnaryOperator operator) { + Objects.requireNonNull(operator); + float state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsFloat(state, values[i]); + } + return state; + } + + @Override + public OptionalFloat reduce(FloatFloatUnaryOperator operator) { + Objects.requireNonNull(operator); + float state = 0F; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsFloat(state, values[i]); + } + return empty ? OptionalFloat.empty() : OptionalFloat.of(state); + } + + @Override + public OptionalFloat findFirst(FloatPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalFloat.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalFloat.of(values[i]); + } + return OptionalFloat.empty(); + } + + @Override + public int count(FloatPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2FloatMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2FloatMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements FloatIterator { + @Override + public float nextFloat() { + 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(keys[pos] != null){ + 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 { + Reference2FloatHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0F; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatLinkedHashMap.java new file mode 100644 index 0000000..fc95543 --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2FloatLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntFloatConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectFloatConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2FloatMap; +import speiger.src.collections.objects.maps.interfaces.Object2FloatOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.floats.collections.AbstractFloatCollection; +import speiger.src.collections.floats.collections.FloatOrderedCollection; +import speiger.src.collections.floats.collections.FloatIterator; +import speiger.src.collections.floats.functions.function.FloatFloatUnaryOperator; +import speiger.src.collections.floats.functions.FloatConsumer; +import speiger.src.collections.floats.lists.FloatListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.floats.functions.function.FloatPredicate; +import speiger.src.collections.floats.functions.OptionalFloat; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2FloatLinkedHashMap extends Reference2FloatHashMap implements Object2FloatOrderedMap +{ + /** 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 Reference2FloatLinkedHashMap() { + 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 Reference2FloatLinkedHashMap(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 Reference2FloatLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2FloatLinkedHashMap(T[] keys, Float[] 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 Reference2FloatLinkedHashMap(T[] keys, Float[] 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 Reference2FloatLinkedHashMap(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 Reference2FloatLinkedHashMap(Object2FloatMap 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 Reference2FloatLinkedHashMap(Object2FloatMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public float putAndMoveToFirst(T key, float value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + float 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 float putAndMoveToLast(T key, float value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + float 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 float putFirst(T key, float value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 float putLast(T key, float value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public float getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public float getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(float value) { + int index = firstIndex; + while(index != -1) { + if(Float.floatToIntBits(values[index]) == Float.floatToIntBits(value)) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Float.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2FloatLinkedHashMap copy() { + Reference2FloatLinkedHashMap map = new Reference2FloatLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 float firstFloatValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public float lastFloatValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2FloatMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2FloatMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2FloatMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2FloatMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2FloatEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public FloatOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (FloatOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectFloatConsumer 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]; + float[] newValues = new float[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2FloatOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2FloatMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2FloatMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2FloatMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2FloatMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2FloatMap.Entry o) { + return Reference2FloatLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2FloatMap.Entry o) { + return Reference2FloatLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2FloatMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstFloatValue()); + } + + @Override + public Object2FloatMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastFloatValue()); + } + + @Override + public Object2FloatMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstFloatValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2FloatMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastFloatValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2FloatMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2FloatMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2FloatMap.Entry 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 Object2FloatMap.Entry) { + Object2FloatMap.Entry entry = (Object2FloatMap.Entry)o; + int index = Reference2FloatLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Float.floatToIntBits(entry.getFloatValue()) == Float.floatToIntBits(Reference2FloatLinkedHashMap.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2FloatLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Float.valueOf(Reference2FloatLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2FloatMap.Entry) { + Object2FloatMap.Entry entry = (Object2FloatMap.Entry)o; + return Reference2FloatLinkedHashMap.this.remove(entry.getKey(), entry.getFloatValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2FloatLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2FloatLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2FloatLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2FloatLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2FloatLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2FloatLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2FloatLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2FloatLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractFloatCollection implements FloatOrderedCollection { + @Override + public boolean contains(float e) { return containsValue(e); } + + @Override + public boolean add(float o) { throw new UnsupportedOperationException(); } + @Override + public FloatIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2FloatLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2FloatLinkedHashMap.this.clear(); } + @Override + public FloatOrderedCollection reversed() { return new AbstractFloatCollection.ReverseFloatOrderedCollection(this, this::reverseIterator); } + private FloatIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(float e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(float e) { throw new UnsupportedOperationException(); } + @Override + public float getFirstFloat() { return firstFloatValue(); } + @Override + public float removeFirstFloat() { + float result = firstFloatValue(); + pollFirstKey(); + return result; + } + @Override + public float getLastFloat() { return lastFloatValue(); } + @Override + public float removeLastFloat() { + float result = lastFloatValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(FloatConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntFloatConsumer 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, ObjectFloatConsumer 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(FloatPredicate 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(FloatPredicate 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(FloatPredicate 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 float reduce(float identity, FloatFloatUnaryOperator operator) { + Objects.requireNonNull(operator); + float state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsFloat(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalFloat reduce(FloatFloatUnaryOperator operator) { + Objects.requireNonNull(operator); + float state = 0F; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsFloat(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalFloat.empty() : OptionalFloat.of(state); + } + + @Override + public OptionalFloat findFirst(FloatPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalFloat.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalFloat.of(values[index]); + index = (int)links[index]; + } + return OptionalFloat.empty(); + } + + @Override + public int count(FloatPredicate 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(T from) { + super(from); + } + + @Override + public Object2FloatMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2FloatMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2FloatMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2FloatMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2FloatMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2FloatMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2FloatMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2FloatMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements FloatListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public float previousFloat() { + return values[previousEntry()]; + } + + @Override + public float nextFloat() { + return values[nextEntry()]; + } + + @Override + public void set(float e) { throw new UnsupportedOperationException(); } + + @Override + public void add(float 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = 0F; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0F; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntHashMap.java new file mode 100644 index 0000000..dd6931f --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; +import java.util.function.IntPredicate; +import java.util.OptionalInt; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntIntConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectIntConsumer; +import speiger.src.collections.objects.functions.function.ToIntFunction; +import speiger.src.collections.objects.functions.function.ObjectIntUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2IntMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.collections.ints.collections.AbstractIntCollection; +import speiger.src.collections.ints.collections.IntCollection; +import speiger.src.collections.ints.functions.IntSupplier; +import speiger.src.collections.ints.functions.function.IntIntUnaryOperator; + +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.functions.IntConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2IntHashMap extends AbstractObject2IntMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient int[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient IntCollection 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 Reference2IntHashMap() { + 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 Reference2IntHashMap(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 Reference2IntHashMap(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 int[this.capacity]; + } + + /** + * 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 Reference2IntHashMap(T[] keys, Integer[] 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 Reference2IntHashMap(T[] keys, Integer[] 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 Reference2IntHashMap(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 Reference2IntHashMap(Object2IntMap 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 Reference2IntHashMap(Object2IntMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public int put(T key, int value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + int oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public int putIfAbsent(T key, int value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(values[slot] == getDefaultReturnValue()) { + int oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public int addTo(T key, int value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + int oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public int subFrom(T key, int value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + int oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(int value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && values[i] == value) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Integer.valueOf(values[i])))) return true; + return false; + } + + @Override + public int rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public int remOrDefault(T key, int defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Integer remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Integer.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, int value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Integer.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Integer.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public int getInt(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Integer get(Object key) { + int slot = findIndex(key); + return Integer.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public int getOrDefault(T key, int defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2IntHashMap copy() { + Reference2IntHashMap map = new Reference2IntHashMap<>(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> object2IntEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public IntCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectIntConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, int oldValue, int newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public int replace(T key, int value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + int oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public int computeInt(T key, ObjectIntUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + int newValue = mappingFunction.applyAsInt(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + int newValue = mappingFunction.applyAsInt(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public int computeIntIfAbsent(T key, ToIntFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + int newValue = mappingFunction.applyAsInt(key); + insert(-index-1, key, newValue); + return newValue; + } + int newValue = values[index]; + return newValue; + } + + @Override + public int supplyIntIfAbsent(T key, IntSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + int newValue = valueProvider.getAsInt(); + insert(-index-1, key, newValue); + return newValue; + } + int newValue = values[index]; + return newValue; + } + + @Override + public int computeIntIfPresent(T key, ObjectIntUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + int newValue = mappingFunction.applyAsInt(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public int computeIntNonDefault(T key, ObjectIntUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + int newValue = mappingFunction.applyAsInt(key, getDefaultReturnValue()); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + int newValue = mappingFunction.applyAsInt(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public int computeIntIfAbsentNonDefault(T key, ToIntFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + int newValue = mappingFunction.applyAsInt(key); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + int newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = mappingFunction.applyAsInt(key); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public int supplyIntIfAbsentNonDefault(T key, IntSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + int newValue = valueProvider.getAsInt(); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + int newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = valueProvider.getAsInt(); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public int computeIntIfPresentNonDefault(T key, ObjectIntUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || values[index] == getDefaultReturnValue()) return getDefaultReturnValue(); + int newValue = mappingFunction.applyAsInt(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public int mergeInt(T key, int value, IntIntUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + int newValue = index < 0 || values[index] == getDefaultReturnValue() ? value : mappingFunction.applyAsInt(values[index], value); + if(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 mergeAllInt(Object2IntMap m, IntIntUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2IntMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + int newValue = index < 0 || values[index] == getDefaultReturnValue() ? entry.getIntValue() : mappingFunction.applyAsInt(values[index], entry.getIntValue()); + if(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected int removeIndex(int pos) { + int value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = 0; + 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, T key, int 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]; + int[] newValues = new int[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0; + 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 T key; + protected int value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public int getIntValue() { + return value; + } + + @Override + public int setValue(int value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2IntMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public int getIntValue() { + return values[index]; + } + + @Override + public int setValue(int value) { + int oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2IntMap.Entry) { + Object2IntMap.Entry entry = (Object2IntMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && getIntValue() == entry.getIntValue(); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Integer && Objects.equals(getKey(), key) && getIntValue() == ((Integer)value).intValue(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Integer.hashCode(getIntValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Integer.toString(getIntValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2IntMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2IntMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2IntMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2IntHashMap.this.size(); + } + + @Override + public void clear() { + Reference2IntHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2IntMap.Entry) { + Object2IntMap.Entry entry = (Object2IntMap.Entry)o; + int index = Reference2IntHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getIntValue() == Reference2IntHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2IntHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Integer.valueOf(Reference2IntHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2IntMap.Entry) { + Object2IntMap.Entry entry = (Object2IntMap.Entry)o; + return Reference2IntHashMap.this.remove(entry.getKey(), entry.getIntValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2IntHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2IntHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2IntHashMap.this.size(); + } + + @Override + public void clear() { + Reference2IntHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractIntCollection { + @Override + public boolean contains(int e) { + return containsValue(e); + } + + @Override + public boolean add(int o) { + throw new UnsupportedOperationException(); + } + + @Override + public IntIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2IntHashMap.this.size(); + } + + @Override + public void clear() { + Reference2IntHashMap.this.clear(); + } + + @Override + public void forEach(IntConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntIntConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectIntConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(IntPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(IntPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(IntPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public int reduce(int identity, IntIntUnaryOperator operator) { + Objects.requireNonNull(operator); + int state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsInt(state, values[i]); + } + return state; + } + + @Override + public OptionalInt reduce(IntIntUnaryOperator operator) { + Objects.requireNonNull(operator); + int state = 0; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsInt(state, values[i]); + } + return empty ? OptionalInt.empty() : OptionalInt.of(state); + } + + @Override + public OptionalInt findFirst(IntPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalInt.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalInt.of(values[i]); + } + return OptionalInt.empty(); + } + + @Override + public int count(IntPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2IntMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2IntMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements IntIterator { + @Override + public int nextInt() { + 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(keys[pos] != null){ + 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 { + Reference2IntHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntLinkedHashMap.java new file mode 100644 index 0000000..cf35b7b --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2IntLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; +import java.util.function.IntPredicate; +import java.util.OptionalInt; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntIntConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectIntConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2IntMap; +import speiger.src.collections.objects.maps.interfaces.Object2IntOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.ints.collections.AbstractIntCollection; +import speiger.src.collections.ints.collections.IntOrderedCollection; +import speiger.src.collections.ints.collections.IntIterator; +import speiger.src.collections.ints.functions.function.IntIntUnaryOperator; +import speiger.src.collections.ints.functions.IntConsumer; +import speiger.src.collections.ints.lists.IntListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2IntLinkedHashMap extends Reference2IntHashMap implements Object2IntOrderedMap +{ + /** 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 Reference2IntLinkedHashMap() { + 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 Reference2IntLinkedHashMap(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 Reference2IntLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2IntLinkedHashMap(T[] keys, Integer[] 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 Reference2IntLinkedHashMap(T[] keys, Integer[] 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 Reference2IntLinkedHashMap(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 Reference2IntLinkedHashMap(Object2IntMap 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 Reference2IntLinkedHashMap(Object2IntMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public int putAndMoveToFirst(T key, int value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + int 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 int putAndMoveToLast(T key, int value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + int 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 int putFirst(T key, int value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 int putLast(T key, int value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public int getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public int getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(int value) { + int index = firstIndex; + while(index != -1) { + if(values[index] == value) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Integer.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2IntLinkedHashMap copy() { + Reference2IntLinkedHashMap map = new Reference2IntLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 int firstIntValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public int lastIntValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2IntMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2IntMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2IntMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2IntMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2IntEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public IntOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (IntOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectIntConsumer 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]; + int[] newValues = new int[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2IntOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2IntMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2IntMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2IntMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2IntMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2IntMap.Entry o) { + return Reference2IntLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2IntMap.Entry o) { + return Reference2IntLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2IntMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstIntValue()); + } + + @Override + public Object2IntMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastIntValue()); + } + + @Override + public Object2IntMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstIntValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2IntMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastIntValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2IntMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2IntMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2IntMap.Entry 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 Object2IntMap.Entry) { + Object2IntMap.Entry entry = (Object2IntMap.Entry)o; + int index = Reference2IntLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getIntValue() == Reference2IntLinkedHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2IntLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Integer.valueOf(Reference2IntLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2IntMap.Entry) { + Object2IntMap.Entry entry = (Object2IntMap.Entry)o; + return Reference2IntLinkedHashMap.this.remove(entry.getKey(), entry.getIntValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2IntLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2IntLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2IntLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2IntLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2IntLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2IntLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2IntLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2IntLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractIntCollection implements IntOrderedCollection { + @Override + public boolean contains(int e) { return containsValue(e); } + + @Override + public boolean add(int o) { throw new UnsupportedOperationException(); } + @Override + public IntIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2IntLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2IntLinkedHashMap.this.clear(); } + @Override + public IntOrderedCollection reversed() { return new AbstractIntCollection.ReverseIntOrderedCollection(this, this::reverseIterator); } + private IntIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(int e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(int e) { throw new UnsupportedOperationException(); } + @Override + public int getFirstInt() { return firstIntValue(); } + @Override + public int removeFirstInt() { + int result = firstIntValue(); + pollFirstKey(); + return result; + } + @Override + public int getLastInt() { return lastIntValue(); } + @Override + public int removeLastInt() { + int result = lastIntValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(IntConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntIntConsumer 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, ObjectIntConsumer 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(IntPredicate 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(IntPredicate 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(IntPredicate 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 int reduce(int identity, IntIntUnaryOperator operator) { + Objects.requireNonNull(operator); + int state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsInt(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalInt reduce(IntIntUnaryOperator operator) { + Objects.requireNonNull(operator); + int state = 0; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsInt(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalInt.empty() : OptionalInt.of(state); + } + + @Override + public OptionalInt findFirst(IntPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalInt.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalInt.of(values[index]); + index = (int)links[index]; + } + return OptionalInt.empty(); + } + + @Override + public int count(IntPredicate 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(T from) { + super(from); + } + + @Override + public Object2IntMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2IntMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2IntMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2IntMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2IntMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2IntMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2IntMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2IntMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements IntListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public int previousInt() { + return values[previousEntry()]; + } + + @Override + public int nextInt() { + return values[nextEntry()]; + } + + @Override + public void set(int e) { throw new UnsupportedOperationException(); } + + @Override + public void add(int 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = 0; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongHashMap.java new file mode 100644 index 0000000..d9e9d0c --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; +import java.util.function.LongPredicate; +import java.util.OptionalLong; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntLongConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectLongConsumer; +import speiger.src.collections.objects.functions.function.ToLongFunction; +import speiger.src.collections.objects.functions.function.ObjectLongUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2LongMap; +import speiger.src.collections.objects.maps.interfaces.Object2LongMap; +import speiger.src.collections.longs.collections.AbstractLongCollection; +import speiger.src.collections.longs.collections.LongCollection; +import speiger.src.collections.longs.functions.LongSupplier; +import speiger.src.collections.longs.functions.function.LongLongUnaryOperator; + +import speiger.src.collections.longs.collections.LongIterator; +import speiger.src.collections.longs.functions.LongConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2LongHashMap extends AbstractObject2LongMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient long[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient LongCollection 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 Reference2LongHashMap() { + 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 Reference2LongHashMap(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 Reference2LongHashMap(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 long[this.capacity]; + } + + /** + * 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 Reference2LongHashMap(T[] keys, Long[] 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 Reference2LongHashMap(T[] keys, Long[] 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 Reference2LongHashMap(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 Reference2LongHashMap(Object2LongMap 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 Reference2LongHashMap(Object2LongMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public long put(T key, long value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + long oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public long putIfAbsent(T key, long value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(values[slot] == getDefaultReturnValue()) { + long oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public long addTo(T key, long value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + long oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public long subFrom(T key, long value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + long oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(long value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && values[i] == value) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Long.valueOf(values[i])))) return true; + return false; + } + + @Override + public long rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public long remOrDefault(T key, long defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Long remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Long.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, long value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Long.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Long.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public long getLong(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Long get(Object key) { + int slot = findIndex(key); + return Long.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public long getOrDefault(T key, long defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2LongHashMap copy() { + Reference2LongHashMap map = new Reference2LongHashMap<>(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> object2LongEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public LongCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectLongConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, long oldValue, long newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public long replace(T key, long value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + long oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public long computeLong(T key, ObjectLongUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + long newValue = mappingFunction.applyAsLong(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + long newValue = mappingFunction.applyAsLong(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public long computeLongIfAbsent(T key, ToLongFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + long newValue = mappingFunction.applyAsLong(key); + insert(-index-1, key, newValue); + return newValue; + } + long newValue = values[index]; + return newValue; + } + + @Override + public long supplyLongIfAbsent(T key, LongSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + long newValue = valueProvider.getAsLong(); + insert(-index-1, key, newValue); + return newValue; + } + long newValue = values[index]; + return newValue; + } + + @Override + public long computeLongIfPresent(T key, ObjectLongUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + long newValue = mappingFunction.applyAsLong(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public long computeLongNonDefault(T key, ObjectLongUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + long newValue = mappingFunction.applyAsLong(key, getDefaultReturnValue()); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + long newValue = mappingFunction.applyAsLong(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public long computeLongIfAbsentNonDefault(T key, ToLongFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + long newValue = mappingFunction.applyAsLong(key); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + long newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = mappingFunction.applyAsLong(key); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public long supplyLongIfAbsentNonDefault(T key, LongSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + long newValue = valueProvider.getAsLong(); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + long newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = valueProvider.getAsLong(); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public long computeLongIfPresentNonDefault(T key, ObjectLongUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || values[index] == getDefaultReturnValue()) return getDefaultReturnValue(); + long newValue = mappingFunction.applyAsLong(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public long mergeLong(T key, long value, LongLongUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + long newValue = index < 0 || values[index] == getDefaultReturnValue() ? value : mappingFunction.applyAsLong(values[index], value); + if(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 mergeAllLong(Object2LongMap m, LongLongUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2LongMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + long newValue = index < 0 || values[index] == getDefaultReturnValue() ? entry.getLongValue() : mappingFunction.applyAsLong(values[index], entry.getLongValue()); + if(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected long removeIndex(int pos) { + long value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = 0L; + 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, T key, long 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]; + long[] newValues = new long[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0L; + 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 T key; + protected long value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public long getLongValue() { + return value; + } + + @Override + public long setValue(long value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2LongMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public long getLongValue() { + return values[index]; + } + + @Override + public long setValue(long value) { + long oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2LongMap.Entry) { + Object2LongMap.Entry entry = (Object2LongMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && getLongValue() == entry.getLongValue(); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Long && Objects.equals(getKey(), key) && getLongValue() == ((Long)value).longValue(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Long.hashCode(getLongValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Long.toString(getLongValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2LongMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2LongMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2LongMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2LongHashMap.this.size(); + } + + @Override + public void clear() { + Reference2LongHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2LongMap.Entry) { + Object2LongMap.Entry entry = (Object2LongMap.Entry)o; + int index = Reference2LongHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getLongValue() == Reference2LongHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2LongHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Long.valueOf(Reference2LongHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2LongMap.Entry) { + Object2LongMap.Entry entry = (Object2LongMap.Entry)o; + return Reference2LongHashMap.this.remove(entry.getKey(), entry.getLongValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2LongHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2LongHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2LongHashMap.this.size(); + } + + @Override + public void clear() { + Reference2LongHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractLongCollection { + @Override + public boolean contains(long e) { + return containsValue(e); + } + + @Override + public boolean add(long o) { + throw new UnsupportedOperationException(); + } + + @Override + public LongIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2LongHashMap.this.size(); + } + + @Override + public void clear() { + Reference2LongHashMap.this.clear(); + } + + @Override + public void forEach(LongConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntLongConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectLongConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(LongPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(LongPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(LongPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public long reduce(long identity, LongLongUnaryOperator operator) { + Objects.requireNonNull(operator); + long state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsLong(state, values[i]); + } + return state; + } + + @Override + public OptionalLong reduce(LongLongUnaryOperator operator) { + Objects.requireNonNull(operator); + long state = 0L; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsLong(state, values[i]); + } + return empty ? OptionalLong.empty() : OptionalLong.of(state); + } + + @Override + public OptionalLong findFirst(LongPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalLong.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalLong.of(values[i]); + } + return OptionalLong.empty(); + } + + @Override + public int count(LongPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2LongMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2LongMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements LongIterator { + @Override + public long nextLong() { + 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(keys[pos] != null){ + 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 { + Reference2LongHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0L; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongLinkedHashMap.java new file mode 100644 index 0000000..0abe12d --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2LongLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; +import java.util.function.LongPredicate; +import java.util.OptionalLong; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntLongConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectLongConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2LongMap; +import speiger.src.collections.objects.maps.interfaces.Object2LongOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.longs.collections.AbstractLongCollection; +import speiger.src.collections.longs.collections.LongOrderedCollection; +import speiger.src.collections.longs.collections.LongIterator; +import speiger.src.collections.longs.functions.function.LongLongUnaryOperator; +import speiger.src.collections.longs.functions.LongConsumer; +import speiger.src.collections.longs.lists.LongListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2LongLinkedHashMap extends Reference2LongHashMap implements Object2LongOrderedMap +{ + /** 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 Reference2LongLinkedHashMap() { + 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 Reference2LongLinkedHashMap(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 Reference2LongLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2LongLinkedHashMap(T[] keys, Long[] 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 Reference2LongLinkedHashMap(T[] keys, Long[] 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 Reference2LongLinkedHashMap(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 Reference2LongLinkedHashMap(Object2LongMap 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 Reference2LongLinkedHashMap(Object2LongMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public long putAndMoveToFirst(T key, long value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + long 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 long putAndMoveToLast(T key, long value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + long 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 long putFirst(T key, long value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 long putLast(T key, long value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public long getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public long getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(long value) { + int index = firstIndex; + while(index != -1) { + if(values[index] == value) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Long.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2LongLinkedHashMap copy() { + Reference2LongLinkedHashMap map = new Reference2LongLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 long firstLongValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public long lastLongValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2LongMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2LongMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2LongMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2LongMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2LongEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public LongOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (LongOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectLongConsumer 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]; + long[] newValues = new long[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2LongOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2LongMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2LongMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2LongMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2LongMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2LongMap.Entry o) { + return Reference2LongLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2LongMap.Entry o) { + return Reference2LongLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2LongMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstLongValue()); + } + + @Override + public Object2LongMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastLongValue()); + } + + @Override + public Object2LongMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstLongValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2LongMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastLongValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2LongMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2LongMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2LongMap.Entry 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 Object2LongMap.Entry) { + Object2LongMap.Entry entry = (Object2LongMap.Entry)o; + int index = Reference2LongLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getLongValue() == Reference2LongLinkedHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2LongLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Long.valueOf(Reference2LongLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2LongMap.Entry) { + Object2LongMap.Entry entry = (Object2LongMap.Entry)o; + return Reference2LongLinkedHashMap.this.remove(entry.getKey(), entry.getLongValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2LongLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2LongLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2LongLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2LongLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2LongLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2LongLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2LongLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2LongLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractLongCollection implements LongOrderedCollection { + @Override + public boolean contains(long e) { return containsValue(e); } + + @Override + public boolean add(long o) { throw new UnsupportedOperationException(); } + @Override + public LongIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2LongLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2LongLinkedHashMap.this.clear(); } + @Override + public LongOrderedCollection reversed() { return new AbstractLongCollection.ReverseLongOrderedCollection(this, this::reverseIterator); } + private LongIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(long e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(long e) { throw new UnsupportedOperationException(); } + @Override + public long getFirstLong() { return firstLongValue(); } + @Override + public long removeFirstLong() { + long result = firstLongValue(); + pollFirstKey(); + return result; + } + @Override + public long getLastLong() { return lastLongValue(); } + @Override + public long removeLastLong() { + long result = lastLongValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(LongConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntLongConsumer 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, ObjectLongConsumer 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(LongPredicate 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(LongPredicate 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(LongPredicate 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 long reduce(long identity, LongLongUnaryOperator operator) { + Objects.requireNonNull(operator); + long state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsLong(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalLong reduce(LongLongUnaryOperator operator) { + Objects.requireNonNull(operator); + long state = 0L; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsLong(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalLong.empty() : OptionalLong.of(state); + } + + @Override + public OptionalLong findFirst(LongPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalLong.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalLong.of(values[index]); + index = (int)links[index]; + } + return OptionalLong.empty(); + } + + @Override + public int count(LongPredicate 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(T from) { + super(from); + } + + @Override + public Object2LongMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2LongMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2LongMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2LongMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2LongMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2LongMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2LongMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2LongMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements LongListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public long previousLong() { + return values[previousEntry()]; + } + + @Override + public long nextLong() { + return values[nextEntry()]; + } + + @Override + public void set(long e) { throw new UnsupportedOperationException(); } + + @Override + public void add(long 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = 0L; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = 0L; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectHashMap.java new file mode 100644 index 0000000..b45f08e --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectHashMap.java @@ -0,0 +1,1241 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; +import speiger.src.collections.objects.functions.function.UnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2ObjectMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.collections.AbstractObjectCollection; +import speiger.src.collections.objects.collections.ObjectCollection; +import speiger.src.collections.objects.functions.ObjectSupplier; +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 + * @param the keyType of elements maintained by this Collection + * @param the keyType of elements maintained by this Collection + */ +public class Reference2ObjectHashMap extends AbstractObject2ObjectMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient V[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient ObjectCollection 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 Reference2ObjectHashMap() { + 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 Reference2ObjectHashMap(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 Reference2ObjectHashMap(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 = (V[])new Object[this.capacity]; + } + + /** + * Helper constructor that allow to create a map from unboxed values + * @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 Reference2ObjectHashMap(T[] keys, V[] values) { + this(keys, values, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a map from unboxed values + * @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 Reference2ObjectHashMap(T[] keys, V[] 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 Reference2ObjectHashMap(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 Reference2ObjectHashMap(Object2ObjectMap 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 Reference2ObjectHashMap(Object2ObjectMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public V put(T key, V value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + V oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public V putIfAbsent(T key, V value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(Objects.equals(values[slot], getDefaultReturnValue())) { + V oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && Objects.equals(value, values[i])) return true; + return false; + } + + @Override + public V rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public V remOrDefault(T key, V defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public V remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, values[pos])) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, values[pos])){ + removeIndex(pos); + return true; + } + } + } + + @Override + public V getObject(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public V get(Object key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public V getOrDefault(Object key, V defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2ObjectHashMap copy() { + Reference2ObjectHashMap map = new Reference2ObjectHashMap<>(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> object2ObjectEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public ObjectCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectObjectConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, V oldValue, V newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public V replace(T key, V value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + V oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public V compute(T key, ObjectObjectUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + V newValue = mappingFunction.apply(key, getDefaultReturnValue()); + if(Objects.equals(newValue, getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + V newValue = mappingFunction.apply(key, values[index]); + if(Objects.equals(newValue, getDefaultReturnValue())) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public V computeIfAbsent(T key, UnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + V newValue = mappingFunction.apply(key); + if(Objects.equals(newValue, getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + V newValue = values[index]; + if(Objects.equals(newValue, getDefaultReturnValue())) { + newValue = mappingFunction.apply(key); + if(Objects.equals(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public V supplyIfAbsent(T key, ObjectSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + V newValue = valueProvider.get(); + if(Objects.equals(newValue, getDefaultReturnValue())) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + V newValue = values[index]; + if(Objects.equals(newValue, getDefaultReturnValue())) { + newValue = valueProvider.get(); + if(Objects.equals(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public V computeIfPresent(T key, ObjectObjectUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || Objects.equals(values[index], getDefaultReturnValue())) return getDefaultReturnValue(); + V newValue = mappingFunction.apply(key, values[index]); + if(Objects.equals(newValue, getDefaultReturnValue())) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public V merge(T key, V value, ObjectObjectUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + Objects.requireNonNull(value); + int index = findIndex(key); + V newValue = index < 0 || Objects.equals(values[index], getDefaultReturnValue()) ? value : mappingFunction.apply(values[index], value); + if(Objects.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 mergeAll(Object2ObjectMap m, ObjectObjectUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2ObjectMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + V newValue = index < 0 || Objects.equals(values[index], getDefaultReturnValue()) ? entry.getValue() : mappingFunction.apply(values[index], entry.getValue()); + if(Objects.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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected V removeIndex(int pos) { + V value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = null; + 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, T key, V 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]; + V[] newValues = (V[])new Object[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = null; + 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 T key; + protected V value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public V getValue() { + return value; + } + + @Override + public V setValue(V value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2ObjectMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public V getValue() { + return values[index]; + } + + @Override + public V setValue(V value) { + V oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2ObjectMap.Entry) { + Object2ObjectMap.Entry entry = (Object2ObjectMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && Objects.equals(getValue(), entry.getValue()); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return Objects.equals(getKey(), key) && Objects.equals(getValue(), value); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Objects.hashCode(getValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Objects.toString(getValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2ObjectMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2ObjectMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2ObjectMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2ObjectHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ObjectHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ObjectMap.Entry) { + Object2ObjectMap.Entry entry = (Object2ObjectMap.Entry)o; + int index = Reference2ObjectHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Reference2ObjectHashMap.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2ObjectHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Reference2ObjectHashMap.this.values[index]); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ObjectMap.Entry) { + Object2ObjectMap.Entry entry = (Object2ObjectMap.Entry)o; + return Reference2ObjectHashMap.this.remove(entry.getKey(), entry.getValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2ObjectHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2ObjectHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2ObjectHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ObjectHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractObjectCollection { + @Override + public boolean contains(Object e) { + return containsValue(e); + } + + @Override + public boolean add(V o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2ObjectHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ObjectHashMap.this.clear(); + } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[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(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) 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(keys[i] == null) continue; + state = operator.apply(state, values[i]); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + V state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.apply(state, values[i]); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return Optional.ofNullable(values[i]); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2ObjectMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2ObjectMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements ObjectIterator { + @Override + public V 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(keys[pos] != null){ + 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 { + Reference2ObjectHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = null; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectLinkedHashMap.java new file mode 100644 index 0000000..5bf03ee --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ObjectLinkedHashMap.java @@ -0,0 +1,1433 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectMap; +import speiger.src.collections.objects.maps.interfaces.Object2ObjectOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.objects.collections.AbstractObjectCollection; +import speiger.src.collections.objects.collections.ObjectOrderedCollection; +import speiger.src.collections.objects.collections.ObjectIterator; +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 + * @param the keyType of elements maintained by this Collection + * @param the keyType of elements maintained by this Collection + */ +public class Reference2ObjectLinkedHashMap extends Reference2ObjectHashMap implements Object2ObjectOrderedMap +{ + /** 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 Reference2ObjectLinkedHashMap() { + 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 Reference2ObjectLinkedHashMap(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 Reference2ObjectLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * Helper constructor that allow to create a map from unboxed values + * @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 Reference2ObjectLinkedHashMap(T[] keys, V[] values) { + this(keys, values, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a map from unboxed values + * @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 Reference2ObjectLinkedHashMap(T[] keys, V[] 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 Reference2ObjectLinkedHashMap(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 Reference2ObjectLinkedHashMap(Object2ObjectMap 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 Reference2ObjectLinkedHashMap(Object2ObjectMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public V putAndMoveToFirst(T key, V value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + V 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 V putAndMoveToLast(T key, V value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + V 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 V putFirst(T key, V value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 V putLast(T key, V value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public V getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public V getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if(Objects.equals(values[index], value)) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2ObjectLinkedHashMap copy() { + Reference2ObjectLinkedHashMap map = new Reference2ObjectLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 V firstValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public V lastValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2ObjectMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2ObjectMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2ObjectMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2ObjectMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2ObjectEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public ObjectOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (ObjectOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectObjectConsumer 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]; + V[] newValues = (V[])new Object[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2ObjectOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2ObjectMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2ObjectMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2ObjectMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2ObjectMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2ObjectMap.Entry o) { + return Reference2ObjectLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2ObjectMap.Entry o) { + return Reference2ObjectLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2ObjectMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstValue()); + } + + @Override + public Object2ObjectMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastValue()); + } + + @Override + public Object2ObjectMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2ObjectMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2ObjectMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2ObjectMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2ObjectMap.Entry 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 Object2ObjectMap.Entry) { + Object2ObjectMap.Entry entry = (Object2ObjectMap.Entry)o; + int index = Reference2ObjectLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Reference2ObjectLinkedHashMap.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2ObjectLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Reference2ObjectLinkedHashMap.this.values[index]); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ObjectMap.Entry) { + Object2ObjectMap.Entry entry = (Object2ObjectMap.Entry)o; + return Reference2ObjectLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2ObjectLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2ObjectLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ObjectLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2ObjectLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2ObjectLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2ObjectLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2ObjectLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ObjectLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractObjectCollection implements ObjectOrderedCollection { + @Override + @Deprecated + public boolean contains(Object e) { return containsValue(e); } + + @Override + public boolean add(V o) { throw new UnsupportedOperationException(); } + @Override + public ObjectIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2ObjectLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2ObjectLinkedHashMap.this.clear(); } + @Override + public ObjectOrderedCollection reversed() { return new AbstractObjectCollection.ReverseObjectOrderedCollection<>(this, this::reverseIterator); } + private ObjectIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(V e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(V e) { throw new UnsupportedOperationException(); } + @Override + public V getFirst() { return firstValue(); } + @Override + public V removeFirst() { + V result = firstValue(); + pollFirstKey(); + return result; + } + @Override + public V getLast() { return lastValue(); } + @Override + public V removeLast() { + V result = lastValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(Consumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + 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++, values[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, values[index]); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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(Predicate 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(Predicate 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + V state = null; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.apply(state, values[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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return Optional.ofNullable(values[index]); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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(T from) { + super(from); + } + + @Override + public Object2ObjectMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2ObjectMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2ObjectMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2ObjectMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2ObjectMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2ObjectMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2ObjectMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2ObjectMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements ObjectListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public V previous() { + return values[previousEntry()]; + } + + @Override + public V next() { + return values[nextEntry()]; + } + + @Override + public void set(V e) { throw new UnsupportedOperationException(); } + + @Override + public void add(V 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = null; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = null; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortHashMap.java new file mode 100644 index 0000000..bde34cc --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortHashMap.java @@ -0,0 +1,1372 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntShortConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectShortConsumer; +import speiger.src.collections.objects.functions.function.ToShortFunction; +import speiger.src.collections.objects.functions.function.ObjectShortUnaryOperator; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.maps.abstracts.AbstractObject2ShortMap; +import speiger.src.collections.objects.maps.interfaces.Object2ShortMap; +import speiger.src.collections.shorts.collections.AbstractShortCollection; +import speiger.src.collections.shorts.collections.ShortCollection; +import speiger.src.collections.shorts.functions.ShortSupplier; +import speiger.src.collections.shorts.functions.function.ShortShortUnaryOperator; + +import speiger.src.collections.shorts.collections.ShortIterator; +import speiger.src.collections.shorts.functions.ShortConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.shorts.functions.function.ShortPredicate; +import speiger.src.collections.shorts.functions.OptionalShort; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2ShortHashMap extends AbstractObject2ShortMap implements ITrimmable +{ + /** The Backing keys array */ + protected transient WeakKey[] keys; + /** The Backing values array */ + protected transient short[] 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 entrySet; + /** KeySet cache */ + protected transient ObjectSet keySet; + /** Values cache */ + protected transient ShortCollection 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 Reference2ShortHashMap() { + 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 Reference2ShortHashMap(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 Reference2ShortHashMap(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 short[this.capacity]; + } + + /** + * 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 Reference2ShortHashMap(T[] keys, Short[] 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 Reference2ShortHashMap(T[] keys, Short[] 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 Reference2ShortHashMap(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 Reference2ShortHashMap(Object2ShortMap 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 Reference2ShortHashMap(Object2ShortMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public short put(T key, short value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + short oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + + @Override + public short putIfAbsent(T key, short value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + else if(values[slot] == getDefaultReturnValue()) { + short oldValue = values[slot]; + values[slot] = value; + return oldValue; + } + return values[slot]; + } + + @Override + public short addTo(T key, short value) { + int slot = findIndex(key); + if(slot < 0) { + insert(-slot-1, key, value); + return getDefaultReturnValue(); + } + short oldValue = values[slot]; + values[slot] += value; + return oldValue; + } + + @Override + public short subFrom(T key, short value) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + short oldValue = values[slot]; + values[slot] -= value; + if(value < 0 ? (values[slot] >= getDefaultReturnValue()) : (values[slot] <= getDefaultReturnValue())) removeIndex(slot); + return oldValue; + } + @Override + public boolean containsKey(Object key) { + return findIndex(key) >= 0; + } + + @Override + public boolean containsValue(short value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && values[i] == value) return true; + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + for(int i = capacity-1;i >= 0;i--) + if(keys[i] != null && ((value == null && values[i] == getDefaultReturnValue()) || Objects.equals(value, Short.valueOf(values[i])))) return true; + return false; + } + + @Override + public short rem(T key) { + int slot = findIndex(key); + if(slot < 0) return getDefaultReturnValue(); + return removeIndex(slot); + } + + @Override + public short remOrDefault(T key, short defaultValue) { + int slot = findIndex(key); + if(slot < 0) return defaultValue; + return removeIndex(slot); + } + + @Override + public Short remove(Object key) { + int slot = findIndex(key); + if(slot < 0) return Short.valueOf(getDefaultReturnValue()); + return removeIndex(slot); + } + + @Override + public boolean remove(T key, short value) { + removeStaleEntries(); + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + WeakKey current = keys[pos]; + if(current == null) return false; + if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(current.get(), key) && value == values[pos]) { + removeIndex(pos); + return true; + } + } + } + + @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(current == null) return false; + if(Objects.equals(key, current.get()) && Objects.equals(value, Short.valueOf(values[pos]))) { + removeIndex(pos); + return true; + } + while(true) { + if((current = keys[pos = (++pos & mask)]) == null) return false; + else if(Objects.equals(key, current.get()) && Objects.equals(value, Short.valueOf(values[pos]))){ + removeIndex(pos); + return true; + } + } + } + + @Override + public short getShort(T key) { + int slot = findIndex(key); + return slot < 0 ? getDefaultReturnValue() : values[slot]; + } + + @Override + public Short get(Object key) { + int slot = findIndex(key); + return Short.valueOf(slot < 0 ? getDefaultReturnValue() : values[slot]); + } + + @Override + public short getOrDefault(T key, short defaultValue) { + int slot = findIndex(key); + return slot < 0 ? defaultValue : values[slot]; + } + + @Override + public Reference2ShortHashMap copy() { + Reference2ShortHashMap map = new Reference2ShortHashMap<>(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> object2ShortEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return entrySet; + } + + @Override + public ObjectSet keySet() { + if(keySet == null) keySet = new KeySet(); + return keySet; + } + + @Override + public ShortCollection values() { + if(valuesC == null) valuesC = new Values(); + return valuesC; + } + + @Override + public void forEach(ObjectShortConsumer action) { + removeStaleEntries(); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(keys[i].get(), values[i]); + } + } + + @Override + public boolean replace(T key, short oldValue, short newValue) { + int index = findIndex(key); + if(index < 0 || values[index] != oldValue) return false; + values[index] = newValue; + return true; + } + + @Override + public short replace(T key, short value) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + short oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public short computeShort(T key, ObjectShortUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + short newValue = mappingFunction.applyAsShort(key, getDefaultReturnValue()); + insert(-index-1, key, newValue); + return newValue; + } + short newValue = mappingFunction.applyAsShort(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public short computeShortIfAbsent(T key, ToShortFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + short newValue = mappingFunction.applyAsShort(key); + insert(-index-1, key, newValue); + return newValue; + } + short newValue = values[index]; + return newValue; + } + + @Override + public short supplyShortIfAbsent(T key, ShortSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + short newValue = valueProvider.getAsShort(); + insert(-index-1, key, newValue); + return newValue; + } + short newValue = values[index]; + return newValue; + } + + @Override + public short computeShortIfPresent(T key, ObjectShortUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + short newValue = mappingFunction.applyAsShort(key, values[index]); + values[index] = newValue; + return newValue; + } + + @Override + public short computeShortNonDefault(T key, ObjectShortUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + short newValue = mappingFunction.applyAsShort(key, getDefaultReturnValue()); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + short newValue = mappingFunction.applyAsShort(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public short computeShortIfAbsentNonDefault(T key, ToShortFunction mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0) { + short newValue = mappingFunction.applyAsShort(key); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + short newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = mappingFunction.applyAsShort(key); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public short supplyShortIfAbsentNonDefault(T key, ShortSupplier valueProvider) { + Objects.requireNonNull(valueProvider); + int index = findIndex(key); + if(index < 0) { + short newValue = valueProvider.getAsShort(); + if(newValue == getDefaultReturnValue()) return newValue; + insert(-index-1, key, newValue); + return newValue; + } + short newValue = values[index]; + if(newValue == getDefaultReturnValue()) { + newValue = valueProvider.getAsShort(); + if(newValue == getDefaultReturnValue()) return newValue; + values[index] = newValue; + } + return newValue; + } + + @Override + public short computeShortIfPresentNonDefault(T key, ObjectShortUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + if(index < 0 || values[index] == getDefaultReturnValue()) return getDefaultReturnValue(); + short newValue = mappingFunction.applyAsShort(key, values[index]); + if(newValue == getDefaultReturnValue()) { + removeIndex(index); + return newValue; + } + values[index] = newValue; + return newValue; + } + + @Override + public short mergeShort(T key, short value, ShortShortUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + int index = findIndex(key); + short newValue = index < 0 || values[index] == getDefaultReturnValue() ? value : mappingFunction.applyAsShort(values[index], value); + if(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 mergeAllShort(Object2ShortMap m, ShortShortUnaryOperator mappingFunction) { + Objects.requireNonNull(mappingFunction); + for(Object2ShortMap.Entry entry : getFastIterable(m)) { + T key = entry.getKey(); + int index = findIndex(key); + short newValue = index < 0 || values[index] == getDefaultReturnValue() ? entry.getShortValue() : mappingFunction.applyAsShort(values[index], entry.getShortValue()); + if(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(current != null) { + if(Objects.equals(key, current.get())) return pos; + while((current = keys[pos = (++pos & mask)]) != null) + if(Objects.equals(key, current.get())) return pos; + } + return -(pos + 1); + } + + protected short removeIndex(int pos) { + short value = values[pos]; + keys[pos].index(-1); + keys[pos] = null; + values[pos] = (short)0; + 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, T key, short 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]; + short[] newValues = new short[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(keys[i] != null) break; + } + if(newKeys[pos = HashUtil.mix(keys[i].hash()) & newMask] != null) + while(newKeys[pos = (++pos & newMask)] != null); + 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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (short)0; + 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 T key; + protected short value; + + public ValueMapEntry(int index) { + super(index); + key = keys[index].get(); + value = values[index]; + } + + @Override + public T getKey() { + return key; + } + + @Override + public short getShortValue() { + return value; + } + + @Override + public short setValue(short value) { + this.value = value; + return super.setValue(value); + } + } + + protected class MapEntry implements Object2ShortMap.Entry, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + void set(int index) { + this.index = index; + } + + @Override + public T getKey() { + return keys[index].get(); + } + + @Override + public short getShortValue() { + return values[index]; + } + + @Override + public short setValue(short value) { + short oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof Object2ShortMap.Entry) { + Object2ShortMap.Entry entry = (Object2ShortMap.Entry)obj; + return Objects.equals(getKey(), entry.getKey()) && getShortValue() == entry.getShortValue(); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); + return value instanceof Short && Objects.equals(getKey(), key) && getShortValue() == ((Short)value).shortValue(); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey()) ^ Short.hashCode(getShortValue()); + } + + @Override + public String toString() { + return Objects.toString(getKey()) + "=" + Short.toString(getShortValue()); + } + } + + private final class MapEntrySet extends AbstractObjectSet> implements Object2ShortMap.FastEntrySet { + @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(keys[i] != null) action.accept(new ValueMapEntry(i)); + } + + @Override + public void fastForEach(Consumer> action) { + MapEntry entry = new MapEntry(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) { + 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(keys[i] != null) 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(keys[i] != null) 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(keys[i] != null) { + 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(!filter.test(entry)) return false; + } + } + return true; + } + + @Override + public E reduce(E identity, BiFunction, E> operator) { + Objects.requireNonNull(operator); + E state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.apply(state, new ValueMapEntry(i)); + } + return state; + } + + @Override + public Optional> reduce(ObjectObjectUnaryOperator, Object2ShortMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2ShortMap.Entry state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) 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(keys[i] != null) { + 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(keys[i] != null) { + entry.set(i); + if(filter.test(entry)) result++; + } + } + return result; + } + + @Override + public int size() { + return Reference2ShortHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ShortHashMap.this.clear(); + } + + @Override + public boolean contains(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ShortMap.Entry) { + Object2ShortMap.Entry entry = (Object2ShortMap.Entry)o; + int index = Reference2ShortHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getShortValue() == Reference2ShortHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2ShortHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Short.valueOf(Reference2ShortHashMap.this.values[index])); + } + } + return false; + } + + @Override + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ShortMap.Entry) { + Object2ShortMap.Entry entry = (Object2ShortMap.Entry)o; + return Reference2ShortHashMap.this.remove(entry.getKey(), entry.getShortValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2ShortHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + } + + private final class KeySet extends AbstractObjectSet { + @Override + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2ShortHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { + throw new UnsupportedOperationException(); + } + + @Override + public ObjectIterator iterator() { + return new KeyIterator(); + } + + @Override + public int size() { + return Reference2ShortHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ShortHashMap.this.clear(); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public void forEach(Consumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(keys[i].get()); + } + + @Override + public void forEachIndexed(IntObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, keys[i].get()); + } + } + + @Override + public void forEach(E input, ObjectObjectConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, keys[i].get()); + } + } + + @Override + public boolean matchesAny(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return true; + } + return false; + } + + @Override + public boolean matchesNone(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return false; + } + return true; + } + + @Override + public boolean matchesAll(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(keys[i].get())) 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(keys[i] == null) continue; + state = operator.apply(state, keys[i].get()); + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = keys[i].get(); + continue; + } + state = operator.apply(state, keys[i].get()); + } + return empty ? Optional.empty() : Optional.ofNullable(state); + } + + @Override + public Optional findFirst(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return Optional.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) return Optional.ofNullable(keys[i].get()); + } + return Optional.empty(); + } + + @Override + public int count(Predicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(keys[i].get())) result++; + } + return result; + } + } + + private class Values extends AbstractShortCollection { + @Override + public boolean contains(short e) { + return containsValue(e); + } + + @Override + public boolean add(short o) { + throw new UnsupportedOperationException(); + } + + @Override + public ShortIterator iterator() { + return new ValueIterator(); + } + + @Override + public int size() { + return Reference2ShortHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ShortHashMap.this.clear(); + } + + @Override + public void forEach(ShortConsumer action) { + for(int i = capacity-1;i>=0;i--) + if(keys[i] != null) action.accept(values[i]); + } + + @Override + public void forEachIndexed(IntShortConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1, index = 0;i>=0;i--) { + if(keys[i] != null) action.accept(index++, values[i]); + } + } + + @Override + public void forEach(E input, ObjectShortConsumer action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null) action.accept(input, values[i]); + } + } + + @Override + public boolean matchesAny(ShortPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(ShortPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(ShortPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && !filter.test(values[i])) return false; + } + return true; + } + + @Override + public short reduce(short identity, ShortShortUnaryOperator operator) { + Objects.requireNonNull(operator); + short state = identity; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + state = operator.applyAsShort(state, values[i]); + } + return state; + } + + @Override + public OptionalShort reduce(ShortShortUnaryOperator operator) { + Objects.requireNonNull(operator); + short state = (short)0; + boolean empty = true; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] == null) continue; + if(empty) { + empty = false; + state = values[i]; + continue; + } + state = operator.applyAsShort(state, values[i]); + } + return empty ? OptionalShort.empty() : OptionalShort.of(state); + } + + @Override + public OptionalShort findFirst(ShortPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalShort.empty(); + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) return OptionalShort.of(values[i]); + } + return OptionalShort.empty(); + } + + @Override + public int count(ShortPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + for(int i = capacity-1;i>=0;i--) { + if(keys[i] != null && filter.test(values[i])) result++; + } + return result; + } + } + + private class FastEntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry = new MapEntry(); + @Override + public Object2ShortMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + } + + private class EntryIterator extends MapIterator implements ObjectIterator> { + MapEntry entry; + @Override + public Object2ShortMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + } + + private class KeyIterator extends MapIterator implements ObjectIterator { + @Override + public T next() { + return keys[nextEntry()].get(); + } + } + + private class ValueIterator extends MapIterator implements ShortIterator { + @Override + public short nextShort() { + 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(keys[pos] != null){ + 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 { + Reference2ShortHashMap.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((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (short)0; + 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/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortLinkedHashMap.java b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortLinkedHashMap.java new file mode 100644 index 0000000..ff9fda8 --- /dev/null +++ b/src/main/java/speiger/src/collections/objects/maps/impl/reference/Reference2ShortLinkedHashMap.java @@ -0,0 +1,1474 @@ +package speiger.src.collections.objects.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; + +import speiger.src.collections.objects.collections.ObjectBidirectionalIterator; +import speiger.src.collections.ints.functions.consumer.IntObjectConsumer; +import speiger.src.collections.ints.functions.consumer.IntShortConsumer; +import speiger.src.collections.objects.functions.consumer.ObjectShortConsumer; +import speiger.src.collections.objects.functions.function.ObjectObjectUnaryOperator; +import speiger.src.collections.objects.lists.ObjectListIterator; +import speiger.src.collections.objects.maps.interfaces.Object2ShortMap; +import speiger.src.collections.objects.maps.interfaces.Object2ShortOrderedMap; +import speiger.src.collections.objects.sets.AbstractObjectSet; +import speiger.src.collections.objects.sets.ObjectOrderedSet; +import speiger.src.collections.shorts.collections.AbstractShortCollection; +import speiger.src.collections.shorts.collections.ShortOrderedCollection; +import speiger.src.collections.shorts.collections.ShortIterator; +import speiger.src.collections.shorts.functions.function.ShortShortUnaryOperator; +import speiger.src.collections.shorts.functions.ShortConsumer; +import speiger.src.collections.shorts.lists.ShortListIterator; +import speiger.src.collections.objects.functions.consumer.ObjectObjectConsumer; + +import speiger.src.collections.shorts.functions.function.ShortPredicate; +import speiger.src.collections.shorts.functions.OptionalShort; +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 + * @param the keyType of elements maintained by this Collection + */ +public class Reference2ShortLinkedHashMap extends Reference2ShortHashMap implements Object2ShortOrderedMap +{ + /** 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 Reference2ShortLinkedHashMap() { + 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 Reference2ShortLinkedHashMap(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 Reference2ShortLinkedHashMap(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[capacity + 1]; + } + + /** + * 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 Reference2ShortLinkedHashMap(T[] keys, Short[] 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 Reference2ShortLinkedHashMap(T[] keys, Short[] 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 Reference2ShortLinkedHashMap(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 Reference2ShortLinkedHashMap(Object2ShortMap 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 Reference2ShortLinkedHashMap(Object2ShortMap map, float loadFactor) { + this(map.size(), loadFactor); + putAll(map); + } + + @Override + public short putAndMoveToFirst(T key, short value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + short 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 short putAndMoveToLast(T key, short value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + short 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 short putFirst(T key, short value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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 short putLast(T key, short value) { + Objects.requireNonNull(key); + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.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(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(firstKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToFirstIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public boolean moveToLast(T key) { + Objects.requireNonNull(key); + if(isEmpty() || Objects.equals(lastKey(), key)) return false; + int pos = HashUtil.mix(Objects.hashCode(key)) & mask; + while(keys[pos] != null) { + if(Objects.equals(keys[pos].get(), key)) { + moveToLastIndex(pos, false); + return true; + } + pos = ++pos & mask; + } + return false; + } + + @Override + public short getAndMoveToFirst(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToFirstIndex(index, false); + return values[index]; + } + + @Override + public short getAndMoveToLast(T key) { + int index = findIndex(key); + if(index < 0) return getDefaultReturnValue(); + moveToLastIndex(index, false); + return values[index]; + } + + @Override + public boolean containsValue(short value) { + int index = firstIndex; + while(index != -1) { + if(values[index] == value) return true; + index = (int)links[index]; + } + return false; + } + + @Override + @Deprecated + public boolean containsValue(Object value) { + int index = firstIndex; + while(index != -1) { + if((value == null && values[index] == getDefaultReturnValue()) || Objects.equals(value, Short.valueOf(values[index]))) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public Reference2ShortLinkedHashMap copy() { + Reference2ShortLinkedHashMap map = new Reference2ShortLinkedHashMap<>(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 T firstKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex].get(); + } + + @Override + public T pollFirstKey() { + 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 T lastKey() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex].get(); + } + + @Override + public T pollLastKey() { + 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 short firstShortValue() { + if(size == 0) throw new NoSuchElementException(); + return values[firstIndex]; + } + + @Override + public short lastShortValue() { + if(size == 0) throw new NoSuchElementException(); + return values[lastIndex]; + } + + @Override + public Object2ShortMap.Entry firstEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[firstIndex].get(), values[firstIndex]); + } + + @Override + public Object2ShortMap.Entry lastEntry() { + if(size == 0) throw new NoSuchElementException(); + return new BasicEntry<>(keys[lastIndex].get(), values[lastIndex]); + } + + @Override + public Object2ShortMap.Entry pollFirstEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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 Object2ShortMap.Entry pollLastEntry() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + BasicEntry result = new BasicEntry<>(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> object2ShortEntrySet() { + if(entrySet == null) entrySet = new MapEntrySet(); + return (ObjectOrderedSet>)entrySet; + } + + @Override + public ObjectOrderedSet keySet() { + if(keySet == null) keySet = new KeySet(); + return (ObjectOrderedSet)keySet; + } + + @Override + public ShortOrderedCollection values() { + if(valuesC == null) valuesC = new Values(); + return (ShortOrderedCollection)valuesC; + } + + @Override + public void forEach(ObjectShortConsumer 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]; + short[] newValues = new short[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(keys[i] == null) pos = newSize; + else { + pos = HashUtil.mix(keys[i].hash()) & newMask; + while(newKeys[pos] != null) 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 Object2ShortOrderedMap.FastOrderedSet { + @Override + public void addFirst(Object2ShortMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public void addLast(Object2ShortMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToFirst(Object2ShortMap.Entry o) { throw new UnsupportedOperationException(); } + @Override + public boolean addAndMoveToLast(Object2ShortMap.Entry o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(Object2ShortMap.Entry o) { + return Reference2ShortLinkedHashMap.this.moveToFirst(o.getKey()); + } + + @Override + public boolean moveToLast(Object2ShortMap.Entry o) { + return Reference2ShortLinkedHashMap.this.moveToLast(o.getKey()); + } + + @Override + public Object2ShortMap.Entry getFirst() { + return new BasicEntry<>(firstKey(), firstShortValue()); + } + + @Override + public Object2ShortMap.Entry getLast() { + return new BasicEntry<>(lastKey(), lastShortValue()); + } + + @Override + public Object2ShortMap.Entry removeFirst() { + BasicEntry entry = new BasicEntry<>(firstKey(), firstShortValue()); + pollFirstKey(); + return entry; + } + + @Override + public Object2ShortMap.Entry removeLast() { + BasicEntry entry = new BasicEntry<>(lastKey(), lastShortValue()); + pollLastKey(); + return entry; + } + + @Override + public ObjectBidirectionalIterator> iterator() { + return new EntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> reverseIterator() { + return new EntryIterator(false); + } + + @Override + public ObjectBidirectionalIterator> iterator(Object2ShortMap.Entry fromElement) { + return new EntryIterator(fromElement.getKey()); + } + + @Override + public ObjectBidirectionalIterator> fastIterator() { + return new FastEntryIterator(true); + } + + @Override + public ObjectBidirectionalIterator> fastIterator(T 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, E> 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, Object2ShortMap.Entry> operator) { + Objects.requireNonNull(operator); + Object2ShortMap.Entry 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 Object2ShortMap.Entry) { + Object2ShortMap.Entry entry = (Object2ShortMap.Entry)o; + int index = Reference2ShortLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return entry.getShortValue() == Reference2ShortLinkedHashMap.this.values[index]; + } + else { + Map.Entry entry = (Map.Entry)o; + int index = Reference2ShortLinkedHashMap.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), Short.valueOf(Reference2ShortLinkedHashMap.this.values[index])); + } + } + return false; + } + + @Override + @Deprecated + public boolean remove(Object o) { + if(o instanceof Map.Entry) { + if(o instanceof Object2ShortMap.Entry) { + Object2ShortMap.Entry entry = (Object2ShortMap.Entry)o; + return Reference2ShortLinkedHashMap.this.remove(entry.getKey(), entry.getShortValue()); + } + Map.Entry entry = (Map.Entry)o; + return Reference2ShortLinkedHashMap.this.remove(entry.getKey(), entry.getValue()); + } + return false; + } + + @Override + public int size() { + return Reference2ShortLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ShortLinkedHashMap.this.clear(); + } + } + + private final class KeySet extends AbstractObjectSet implements ObjectOrderedSet { + @Override + @Deprecated + public boolean contains(Object e) { + return containsKey(e); + } + + @Override + public boolean remove(Object o) { + int oldSize = size; + Reference2ShortLinkedHashMap.this.remove(o); + return size != oldSize; + } + + @Override + public boolean add(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public void addLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToFirst(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean addAndMoveToLast(T o) { throw new UnsupportedOperationException(); } + + @Override + public boolean moveToFirst(T o) { + return Reference2ShortLinkedHashMap.this.moveToFirst(o); + } + + @Override + public boolean moveToLast(T o) { + return Reference2ShortLinkedHashMap.this.moveToLast(o); + } + + @Override + public ObjectListIterator iterator() { + return new KeyIterator(true); + } + + @Override + public ObjectListIterator reverseIterator() { + return new KeyIterator(false); + } + + @Override + public ObjectBidirectionalIterator iterator(T fromElement) { + return new KeyIterator(fromElement); + } + + @Override + public KeySet copy() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return Reference2ShortLinkedHashMap.this.size(); + } + + @Override + public void clear() { + Reference2ShortLinkedHashMap.this.clear(); + } + + @Override + public T getFirst() { + return firstKey(); + } + + @Override + public T removeFirst() { + return pollFirstKey(); + } + + @Override + public T getLast() { + return lastKey(); + } + + @Override + public T removeLast() { + return pollLastKey(); + } + + @Override + public void forEach(Consumer action) { + int index = firstIndex; + while(index != -1){ + action.accept(keys[index].get()); + 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++, keys[index].get()); + 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, keys[index].get()); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(Predicate 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 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 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 E reduce(E identity, BiFunction operator) { + Objects.requireNonNull(operator); + E state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.apply(state, keys[index].get()); + index = (int)links[index]; + } + return state; + } + + @Override + public Optional reduce(ObjectObjectUnaryOperator operator) { + Objects.requireNonNull(operator); + T state = null; + 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(state, keys[index].get()); + 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(); + int index = firstIndex; + while(index != -1){ + if(filter.test(keys[index].get())) return Optional.ofNullable(keys[index].get()); + index = (int)links[index]; + } + return Optional.empty(); + } + + @Override + public int count(Predicate 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 AbstractShortCollection implements ShortOrderedCollection { + @Override + public boolean contains(short e) { return containsValue(e); } + + @Override + public boolean add(short o) { throw new UnsupportedOperationException(); } + @Override + public ShortIterator iterator() { return new ValueIterator(true); } + @Override + public int size() { return Reference2ShortLinkedHashMap.this.size(); } + @Override + public void clear() { Reference2ShortLinkedHashMap.this.clear(); } + @Override + public ShortOrderedCollection reversed() { return new AbstractShortCollection.ReverseShortOrderedCollection(this, this::reverseIterator); } + private ShortIterator reverseIterator() { + return new ValueIterator(false); + } + @Override + public void addFirst(short e) { throw new UnsupportedOperationException(); } + @Override + public void addLast(short e) { throw new UnsupportedOperationException(); } + @Override + public short getFirstShort() { return firstShortValue(); } + @Override + public short removeFirstShort() { + short result = firstShortValue(); + pollFirstKey(); + return result; + } + @Override + public short getLastShort() { return lastShortValue(); } + @Override + public short removeLastShort() { + short result = lastShortValue(); + pollLastKey(); + return result; + } + @Override + public void forEach(ShortConsumer action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(values[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(IntShortConsumer 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, ObjectShortConsumer 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(ShortPredicate 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(ShortPredicate 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(ShortPredicate 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 short reduce(short identity, ShortShortUnaryOperator operator) { + Objects.requireNonNull(operator); + short state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.applyAsShort(state, values[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public OptionalShort reduce(ShortShortUnaryOperator operator) { + Objects.requireNonNull(operator); + short state = (short)0; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + empty = false; + state = values[index]; + index = (int)links[index]; + continue; + } + state = operator.applyAsShort(state, values[index]); + index = (int)links[index]; + } + return empty ? OptionalShort.empty() : OptionalShort.of(state); + } + + @Override + public OptionalShort findFirst(ShortPredicate filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return OptionalShort.empty(); + int index = firstIndex; + while(index != -1){ + if(filter.test(values[index])) return OptionalShort.of(values[index]); + index = (int)links[index]; + } + return OptionalShort.empty(); + } + + @Override + public int count(ShortPredicate 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(T from) { + super(from); + } + + @Override + public Object2ShortMap.Entry next() { + entry.index = nextEntry(); + return entry; + } + + @Override + public Object2ShortMap.Entry previous() { + entry.index = previousEntry(); + return entry; + } + + @Override + public void set(Object2ShortMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2ShortMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class EntryIterator extends MapIterator implements ObjectListIterator> { + MapEntry entry; + + public EntryIterator(boolean start) { super(start); } + public EntryIterator(T from) { + super(from); + } + + @Override + public Object2ShortMap.Entry next() { + return entry = new ValueMapEntry(nextEntry()); + } + + @Override + public Object2ShortMap.Entry previous() { + return entry = new ValueMapEntry(previousEntry()); + } + + @Override + public void remove() { + super.remove(); + entry.index = -1; + } + + @Override + public void set(Object2ShortMap.Entry entry) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object2ShortMap.Entry entry) { throw new UnsupportedOperationException(); } + } + + private class KeyIterator extends MapIterator implements ObjectListIterator { + + public KeyIterator(boolean start) { super(start); } + public KeyIterator(T from) { + super(from); + } + + @Override + public T previous() { + return keys[previousEntry()].get(); + } + + @Override + public T next() { + return keys[nextEntry()].get(); + } + + @Override + public void set(T e) { throw new UnsupportedOperationException(); } + @Override + public void add(T e) { throw new UnsupportedOperationException(); } + } + + private class ValueIterator extends MapIterator implements ShortListIterator { + public ValueIterator(boolean start) { super(start); } + + @Override + public short previousShort() { + return values[previousEntry()]; + } + + @Override + public short nextShort() { + return values[nextEntry()]; + } + + @Override + public void set(short e) { throw new UnsupportedOperationException(); } + + @Override + public void add(short 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(T from) { + Objects.requireNonNull(from); + if(keys[lastIndex] == from) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(Objects.hashCode(from)) & mask; + WeakKey refKey = null; + while((refKey = keys[pos]) != null) { + if(Objects.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] = null; + values[capacity] = (short)0; + } + else { + int slot, last, startPos = current; + current = -1; + WeakKey current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if((current = keys[startPos]) == null) { + keys[last] = null; + values[last] = (short)0; + 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/main/java/speiger/src/collections/objects/utils/ObjectArrays.java b/src/main/java/speiger/src/collections/objects/utils/ObjectArrays.java index b909737..db83bf3 100644 --- a/src/main/java/speiger/src/collections/objects/utils/ObjectArrays.java +++ b/src/main/java/speiger/src/collections/objects/utils/ObjectArrays.java @@ -8,6 +8,7 @@ import java.util.function.IntFunction; import speiger.src.collections.objects.collections.ObjectIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -252,6 +253,69 @@ public class ObjectArrays 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 + * @param the keyType of array that the operation should be applied + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static T[] indirectShuffle(T[] 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 + * @param the keyType of array that the operation should be applied + * @return the provided sorted array + */ + public static T[] indirectShuffle(T[] 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 + * @param the keyType of array that the operation should be applied + * @return the provided sorted array + */ + public static T[] indirectShuffle(T[] 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 + * @param the keyType of array that the operation should be applied + * @return the provided sorted array + */ + public static T[] indirectShuffle(T[] 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); + T t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -490,6 +554,55 @@ public class ObjectArrays } } + /** + * 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 + * @param the keyType of array that the operation should be applied + * @return input array + */ + public static T[] indirectInsertionSort(T[] array, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectInsertionSort(T[] array, int length, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectInsertionSort(T[] array, int from, int to, Comparator 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 @@ -576,6 +689,60 @@ public class ObjectArrays } } + /** + * 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 + * @param the keyType of array that the operation should be applied + * @return input array + */ + public static T[] indirectSelectionSort(T[] array, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectSelectionSort(T[] array, int length, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectSelectionSort(T[] array, int from, int to, Comparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + T 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); + T 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 @@ -675,6 +842,72 @@ public class ObjectArrays } } + /** + * 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 + * @param the keyType of array that the operation should be applied + * @return input array + */ + public static T[] indirectMergeSort(T[] array, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectMergeSort(T[] array, int length, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectMergeSort(T[] array, T[] supp, int from, int to, Comparator 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 @@ -771,6 +1004,56 @@ public class ObjectArrays 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectParallelMergeSort(T[] array, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectParallelMergeSort(T[] array, int length, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectParallelMergeSort(T[] array, T[] supp, int from, int to, Comparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap<>(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 @@ -1124,6 +1407,73 @@ public class ObjectArrays 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 + * @param the keyType of array that the operation should be applied + * @return input array + */ + public static T[] indirectQuickSort(T[] array, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectQuickSort(T[] array, int length, Comparator 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 + * @param the keyType of array that the operation should be applied + */ + public static void indirectQuickSort(T[] array, int from, int to, Comparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + T 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, @@ -1228,6 +1578,58 @@ public class ObjectArrays 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 + * @param the keyType of array that the operation should be applied + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(T[] array, Comparator 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 + * @param the keyType of array that the operation should be applied + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(T[] array, int length, Comparator 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 + * @param the keyType of array that the operation should be applied + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(T[] array, int from, int to, Comparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap<>(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, @@ -1282,6 +1684,18 @@ public class ObjectArrays for(int i = 0;i void swap(T[] a, int from, int to, Swapper swapper) { + swapper.swap(from, to); + T t = a[from]; + a[from] = a[to]; + a[to] = t; + } + + static void swap(T[] a, int from, int to, int length, Swapper swapper) { + to -= length; + for(int i = 0;i int subMedium(T[] data, int a, int b, int c, int length, Comparator comp) { return medium(data, medium(data, a, a + length, a + (length * 2), comp), medium(data, b - length, b, b + length, comp), medium(data, c - (length * 2), c - length, c, comp), comp); } @@ -1304,16 +1718,14 @@ public class ObjectArrays int from; int to; - QuickSortAction(T[] array, int from, int to) - { + QuickSortAction(T[] array, int from, int to) { this.array = array; this.from = from; this.to = to; } @Override - protected void compute() - { + protected void compute() { int length = to - from; if(length <= 0) return; if(length < BASE_THRESHOLD) { @@ -1346,8 +1758,7 @@ public class ObjectArrays int to; Comparator comp; - QuickSortActionComp(T[] array, int from, int to, Comparator comp) - { + QuickSortActionComp(T[] array, int from, int to, Comparator comp) { this.array = array; this.from = from; this.to = to; @@ -1355,8 +1766,7 @@ public class ObjectArrays } @Override - protected void compute() - { + protected void compute() { int length = to - from; if(length <= 0) return; if(length < BASE_THRESHOLD) { @@ -1382,6 +1792,49 @@ public class ObjectArrays } } + static class QuickSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + T[] array; + int from; + int to; + Comparator comp; + Swapper swapper; + + QuickSortActionCompSwap(T[] array, int from, int to, Comparator comp, Swapper swapper) { + this.array = array; + this.from = from; + this.to = to; + this.comp = comp; + this.swapper = swapper; + } + + @Override + protected void compute() { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + T 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(b - a > 1 && d - c > 1) invokeAll(new QuickSortActionCompSwap<>(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap<>(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap<>(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap<>(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; T[] array; @@ -1389,8 +1842,7 @@ public class ObjectArrays int from; int to; - MergeSortAction(T[] array, T[] supp, int from, int to) - { + MergeSortAction(T[] array, T[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1398,8 +1850,7 @@ public class ObjectArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1427,8 +1878,7 @@ public class ObjectArrays int to; Comparator comp; - MergeSortActionComp(T[] array, T[] supp, int from, int to, Comparator comp) - { + MergeSortActionComp(T[] array, T[] supp, int from, int to, Comparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1437,8 +1887,7 @@ public class ObjectArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1458,22 +1907,64 @@ public class ObjectArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + T[] array; + T[] supp; + int from; + int to; + Comparator comp; + Swapper swapper; + + MergeSortActionCompSwap(T[] array, T[] supp, int from, int to, Comparator 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 MergeSortActionCompSwap<>(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap<>(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 extends RecursiveAction { private static final long serialVersionUID = 0L; T[] array; int from; int to; - MemFreeMergeSortAction(T[] array, int from, int to) - { + MemFreeMergeSortAction(T[] 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; @@ -1519,8 +2010,7 @@ public class ObjectArrays int to; Comparator comp; - MemFreeMergeSortActionComp(T[] array, int from, int to, Comparator comp) - { + MemFreeMergeSortActionComp(T[] array, int from, int to, Comparator comp) { this.array = array; this.from = from; this.to = to; @@ -1528,8 +2018,7 @@ public class ObjectArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2BooleanLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2BooleanLinkedOpenHashMap.java index db56e8f..3bc353d 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2BooleanLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2BooleanLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2BooleanLinkedOpenHashMap extends Short2BooleanOpenHashMap imp } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2BooleanLinkedOpenHashMap extends Short2BooleanOpenHashMap imp } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ByteLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ByteLinkedOpenHashMap.java index 01cb7b8..bc1a690 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ByteLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ByteLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2ByteLinkedOpenHashMap extends Short2ByteOpenHashMap implement } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2ByteLinkedOpenHashMap extends Short2ByteOpenHashMap implement } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2CharLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2CharLinkedOpenHashMap.java index 74efd3a..aef525b 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2CharLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2CharLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2CharLinkedOpenHashMap extends Short2CharOpenHashMap implement } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2CharLinkedOpenHashMap extends Short2CharOpenHashMap implement } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2DoubleLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2DoubleLinkedOpenHashMap.java index 59082bb..5b827f4 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2DoubleLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2DoubleLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2DoubleLinkedOpenHashMap extends Short2DoubleOpenHashMap imple } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2DoubleLinkedOpenHashMap extends Short2DoubleOpenHashMap imple } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2FloatLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2FloatLinkedOpenHashMap.java index a8aef0d..be8bcd3 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2FloatLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2FloatLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2FloatLinkedOpenHashMap extends Short2FloatOpenHashMap impleme } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2FloatLinkedOpenHashMap extends Short2FloatOpenHashMap impleme } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2IntLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2IntLinkedOpenHashMap.java index 25e8242..7667cab 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2IntLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2IntLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2IntLinkedOpenHashMap extends Short2IntOpenHashMap implements } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2IntLinkedOpenHashMap extends Short2IntOpenHashMap implements } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2LongLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2LongLinkedOpenHashMap.java index ef1e1e1..5da73cc 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2LongLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2LongLinkedOpenHashMap.java @@ -249,7 +249,7 @@ public class Short2LongLinkedOpenHashMap extends Short2LongOpenHashMap implement } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -273,7 +273,7 @@ public class Short2LongLinkedOpenHashMap extends Short2LongOpenHashMap implement } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ObjectLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ObjectLinkedOpenHashMap.java index c6c86e3..f67b111 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ObjectLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ObjectLinkedOpenHashMap.java @@ -243,7 +243,7 @@ public class Short2ObjectLinkedOpenHashMap extends Short2ObjectOpenHashMap } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -267,7 +267,7 @@ public class Short2ObjectLinkedOpenHashMap extends Short2ObjectOpenHashMap } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ShortLinkedOpenHashMap.java b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ShortLinkedOpenHashMap.java index 7e1f647..dadcc7b 100644 --- a/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ShortLinkedOpenHashMap.java +++ b/src/main/java/speiger/src/collections/shorts/maps/impl/hash/Short2ShortLinkedOpenHashMap.java @@ -241,7 +241,7 @@ public class Short2ShortLinkedOpenHashMap extends Short2ShortOpenHashMap impleme } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } @@ -265,7 +265,7 @@ public class Short2ShortLinkedOpenHashMap extends Short2ShortOpenHashMap impleme } else { int pos = HashUtil.mix(Short.hashCode(key)) & mask; - while(key == (short)0) { + while(keys[pos] != (short)0) { if(keys[pos] == key) return values[pos]; pos = ++pos & mask; } diff --git a/src/main/java/speiger/src/collections/shorts/utils/ShortArrays.java b/src/main/java/speiger/src/collections/shorts/utils/ShortArrays.java index 357ea62..dd71ad2 100644 --- a/src/main/java/speiger/src/collections/shorts/utils/ShortArrays.java +++ b/src/main/java/speiger/src/collections/shorts/utils/ShortArrays.java @@ -7,6 +7,7 @@ import java.util.concurrent.RecursiveAction; import speiger.src.collections.shorts.functions.ShortComparator; import speiger.src.collections.shorts.collections.ShortIterator; import speiger.src.collections.utils.SanityChecks; +import speiger.src.collections.utils.Swapper; /** * A Helper class for Arrays @@ -269,6 +270,65 @@ public class ShortArrays 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 + * @note This uses the SanityChecks#getRandom + * @return the provided sorted array + */ + public static short[] indirectShuffle(short[] 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 + * @return the provided sorted array + */ + public static short[] indirectShuffle(short[] 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 + * @return the provided sorted array + */ + public static short[] indirectShuffle(short[] 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 + * @return the provided sorted array + */ + public static short[] indirectShuffle(short[] 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); + short t = array[j]; + array[j] = array[p]; + array[p] = t; + } + return array; + } + /** * Simple Array Reversal method * @param array the Array that should flip @@ -489,6 +549,52 @@ public class ShortArrays } } + /** + * 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 + * @return input array + */ + public static short[] indirectInsertionSort(short[] array, ShortComparator 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 + */ + public static void indirectInsertionSort(short[] array, int length, ShortComparator 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 + */ + public static void indirectInsertionSort(short[] array, int from, int to, ShortComparator 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 @@ -569,6 +675,57 @@ public class ShortArrays } } + /** + * 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 + * @return input array + */ + public static short[] indirectSelectionSort(short[] array, ShortComparator 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 + */ + public static void indirectSelectionSort(short[] array, int length, ShortComparator 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 + */ + public static void indirectSelectionSort(short[] array, int from, int to, ShortComparator comp, Swapper swapper) { + for (int i = from; i < to; i++) { + short 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); + short 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 @@ -662,6 +819,69 @@ public class ShortArrays } } + /** + * 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 + * @return input array + */ + public static short[] indirectMergeSort(short[] array, ShortComparator 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 + */ + public static void indirectMergeSort(short[] array, int length, ShortComparator 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 + */ + public static void indirectMergeSort(short[] array, short[] supp, int from, int to, ShortComparator 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 @@ -752,6 +972,53 @@ public class ShortArrays 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 + */ + public static void indirectParallelMergeSort(short[] array, ShortComparator 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 + */ + public static void indirectParallelMergeSort(short[] array, int length, ShortComparator 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 + */ + public static void indirectParallelMergeSort(short[] array, short[] supp, int from, int to, ShortComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new MergeSortActionCompSwap(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 @@ -1087,6 +1354,70 @@ public class ShortArrays 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 + * @return input array + */ + public static short[] indirectQuickSort(short[] array, ShortComparator 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 + */ + public static void indirectQuickSort(short[] array, int length, ShortComparator 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 + */ + public static void indirectQuickSort(short[] array, int from, int to, ShortComparator comp, Swapper swapper) { + int length = to - from; + if(length <= 0) return; + if(length < BASE_THRESHOLD) { + indirectSelectionSort(array, from, to, comp, swapper); + return; + } + short 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, @@ -1185,6 +1516,55 @@ public class ShortArrays 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(short[] array, ShortComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(short[] array, int length, ShortComparator 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 + * @note This parallelization is invoked through {@link SanityChecks#invokeTask} which the threadpool can be changed as needed + */ + public static void indirectParallelQuickSort(short[] array, int from, int to, ShortComparator comp, Swapper swapper) { + if(SanityChecks.canParallelTask() && to - from >= PARALLEL_THRESHOLD) { + SanityChecks.invokeTask(new QuickSortActionCompSwap(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, @@ -1236,6 +1616,18 @@ public class ShortArrays 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 QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper), new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper)); + else if(b - a > 1) new QuickSortActionCompSwap(array, from, from + (b - a), comp, swapper).invoke(); + else if(d - c > 1) new QuickSortActionCompSwap(array, to - (d - c), to, comp, swapper).invoke(); + } + } + static class MergeSortAction extends RecursiveAction { private static final long serialVersionUID = 0L; short[] array; @@ -1343,8 +1774,7 @@ public class ShortArrays int from; int to; - MergeSortAction(short[] array, short[] supp, int from, int to) - { + MergeSortAction(short[] array, short[] supp, int from, int to) { this.array = array; this.supp = supp; this.from = from; @@ -1352,8 +1782,7 @@ public class ShortArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to); return; @@ -1381,8 +1810,7 @@ public class ShortArrays int to; ShortComparator comp; - MergeSortActionComp(short[] array, short[] supp, int from, int to, ShortComparator comp) - { + MergeSortActionComp(short[] array, short[] supp, int from, int to, ShortComparator comp) { this.array = array; this.supp = supp; this.from = from; @@ -1391,8 +1819,7 @@ public class ShortArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; @@ -1412,22 +1839,64 @@ public class ShortArrays } } + static class MergeSortActionCompSwap extends RecursiveAction { + private static final long serialVersionUID = 0L; + short[] array; + short[] supp; + int from; + int to; + ShortComparator comp; + Swapper swapper; + + MergeSortActionCompSwap(short[] array, short[] supp, int from, int to, ShortComparator 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 MergeSortActionCompSwap(supp, array, from, mid, comp, swapper), new MergeSortActionCompSwap(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 extends RecursiveAction { private static final long serialVersionUID = 0L; short[] array; int from; int to; - MemFreeMergeSortAction(short[] array, int from, int to) - { + MemFreeMergeSortAction(short[] 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; @@ -1473,8 +1942,7 @@ public class ShortArrays int to; ShortComparator comp; - MemFreeMergeSortActionComp(short[] array, int from, int to, ShortComparator comp) - { + MemFreeMergeSortActionComp(short[] array, int from, int to, ShortComparator comp) { this.array = array; this.from = from; this.to = to; @@ -1482,8 +1950,7 @@ public class ShortArrays } @Override - protected void compute() - { + protected void compute() { if(to - from < BASE_THRESHOLD) { insertionSort(array, from, to, comp); return; 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); +}