From 63ef68fb95918a7a43cd77cf1742002f8c006490 Mon Sep 17 00:00:00 2001 From: Speiger Date: Mon, 5 Jun 2023 23:40:48 +0200 Subject: [PATCH] New Compute function and better toArray implementations --- Changelog.md | 8 + .../templates/collections/Stack.template | 25 + .../maps/abstracts/AbstractMap.template | 91 +- .../templates/maps/interfaces/Map.template | 38 +- .../sets/ImmutableOpenHashSet.template | 1272 ++++++------ .../sets/LinkedOpenCustomHashSet.template | 1758 +++++++++-------- .../templates/sets/LinkedOpenHashSet.template | 1700 ++++++++-------- .../templates/sets/OpenCustomHashSet.template | 1506 +++++++------- .../templates/sets/OpenHashSet.template | 1441 +++++++------- .../speiger/src/collections/utils/Stack.java | 40 + 10 files changed, 4130 insertions(+), 3749 deletions(-) diff --git a/Changelog.md b/Changelog.md index cd59365..edabe6b 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,5 +1,13 @@ # Changelog of versions +### Version 0.8.1 +- Added: getFirst/getLast/removeFirst/removeLast to List.class. +- Added: Dedicated Set toArray implementations. +- Added: ToArray/pushTop functions to Stack.class. +- Added: ComputeNonDefault functions which will contain the current behavior of the Compute function, while the Compute will be changed to be more java compliant! +- Fixed: SetValue wasn't working on forEach implementations. + + ### Version 0.8.0 - Added: getFirst/getLast/removeFirst/removeLast to Lists - Added: Dedicated implementations for toArray into TreeSets diff --git a/src/builder/resources/speiger/assets/collections/templates/collections/Stack.template b/src/builder/resources/speiger/assets/collections/templates/collections/Stack.template index 974a59e..48bd572 100644 --- a/src/builder/resources/speiger/assets/collections/templates/collections/Stack.template +++ b/src/builder/resources/speiger/assets/collections/templates/collections/Stack.template @@ -1,5 +1,7 @@ package speiger.src.collections.PACKAGE.collections; +import java.util.NoSuchElementException; + import speiger.src.collections.utils.Stack; /** @@ -14,6 +16,14 @@ public interface STACK */ public void push(KEY_TYPE e); + /** + * Helper function that pushes the top element on top of the stack again. + * @throws NoSuchElementException if the stack is empty + */ + public default void pushTop() { + push(top()); + } + /** * Removes the Object on top of the stack. * @return the element that is on top of the stack @@ -59,4 +69,19 @@ public interface STACK public default boolean isEmpty() { return size() == 0; } + + /** + * A method to drop the contents of the Stack without clearing the stack + * @Type(E) + * @return the contents of the stack into a seperate array. + */ + public default GENERIC_SPECIAL_KEY_BRACES KEY_SPECIAL_TYPE[] TO_ARRAY() { return TO_ARRAY(NEW_SPECIAL_KEY_ARRAY(size())); } + /** + * A method to drop the contents of the Stack without clearing the stack + * @param input where the elements should be inserted to. If it does not fit then it creates a new appropiatly created array + * @Type(E) + * @return the contents of the stack into a seperate array. + * @note if the Type is generic then a Object Array is created instead of a Type Array + */ + public GENERIC_SPECIAL_KEY_BRACES KEY_SPECIAL_TYPE[] TO_ARRAY(KEY_SPECIAL_TYPE[] input); } \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/abstracts/AbstractMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/abstracts/AbstractMap.template index 7730078..fe65662 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/abstracts/AbstractMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/abstracts/AbstractMap.template @@ -201,6 +201,26 @@ public abstract class ABSTRACT_MAP KEY_VALUE_GENERIC_TYPE extends AbstractMap Previous Entry, Right 32 Bits => Next Entry */ - protected transient long[] links; - /** If a null value is present */ - protected transient boolean containsNull; - /** Index of the Null Value */ - protected transient int nullIndex; - /** Max Index that is allowed to be searched through nullIndex - 1 */ - protected transient int mask; - /** Amount of Elements stored in the HashSet */ - protected int size; - /** The First Index in the Map */ - protected int firstIndex = -1; - /** The Last Index in the Map */ - protected int lastIndex = -1; - - /** - * Helper constructor to optimize the copying - * Only accessible through implementations - */ - protected IMMUTABLE_HASH_SET() {} - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - */ - public IMMUTABLE_HASH_SET(KEY_TYPE[] array) { - this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @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 IMMUTABLE_HASH_SET(KEY_TYPE[] array, float loadFactor) { - this(array, 0, array.length, loadFactor); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public IMMUTABLE_HASH_SET(KEY_TYPE[] array, int offset, int length) { - this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @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 - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public IMMUTABLE_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor) { - init(array, offset, length, loadFactor); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - */ - @Primitive - public IMMUTABLE_HASH_SET(Collection collection) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @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 - */ - @Primitive - public IMMUTABLE_HASH_SET(Collection collection, float loadFactor) { -#if !TYPE_OBJECT - init(ARRAYS.unwrap(collection.toArray(NEW_CLASS_ARRAY(collection.size()))), 0, collection.size(), loadFactor); -#else - init(collection.toArray(NEW_CLASS_ARRAY(collection.size())), 0, collection.size(), loadFactor); -#endif - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - */ - public IMMUTABLE_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @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 IMMUTABLE_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor) { - init(collection.TO_ARRAY(NEW_KEY_ARRAY(collection.size())), 0, collection.size(), loadFactor); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - */ - public IMMUTABLE_HASH_SET(Iterator iterator) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @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 IMMUTABLE_HASH_SET(Iterator iterator, float loadFactor) { - this(ITERATORS.wrap(iterator), loadFactor); - } - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - */ - public IMMUTABLE_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @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 IMMUTABLE_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor) { - KEY_TYPE[] array = ARRAYS.pour(iterator); - init(array, 0, array.length, loadFactor); - } - - protected void init(KEY_TYPE[] a, int offset, int length, float loadFactor) { - SanityChecks.checkArrayCapacity(a.length, offset, length); - int newSize = HashUtil.arraySize(length+1, loadFactor); - int newMask = newSize - 1; - KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); - long[] newLinks = new long[newSize + 1]; - int prev = -1; - for(int i = offset,m=offset+length;i c) { throw new UnsupportedOperationException(); } - @Override - public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { 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) { throw new UnsupportedOperationException(); } - @Override - public boolean moveToLast(KEY_TYPE o) { throw new UnsupportedOperationException(); } - - @Override - public boolean contains(Object o) { -#if TYPE_OBJECT - if(o == null) return containsNull; -#else - if(o == null) return false; - if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return containsNull; -#endif - int pos = HashUtil.mix(o.hashCode()) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NULL(current)) return false; - if(EQUALS_KEY_TYPE(current, o)) return true; - while(true) { - if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; - else if(EQUALS_KEY_TYPE(current, o)) return true; - } - } - - @Override - public KEY_TYPE FIRST_KEY() { - if(size == 0) throw new NoSuchElementException(); - return keys[firstIndex]; - } - - @Override - public KEY_TYPE POLL_FIRST_KEY() { throw new UnsupportedOperationException(); } - - @Override - public KEY_TYPE LAST_KEY() { - if(size == 0) throw new NoSuchElementException(); - return keys[lastIndex]; - } - - @Override - public KEY_TYPE POLL_LAST_KEY() { throw new UnsupportedOperationException(); } - @Override - public boolean remove(Object o) { throw new UnsupportedOperationException(); } - -#if !TYPE_OBJECT - @Override - public boolean contains(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) return containsNull; - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NULL(current)) return false; - if(KEY_EQUALS(current, o)) return true; - while(true) { - if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; - else if(KEY_EQUALS(current, o)) return true; - } - } - - @Override - public boolean remove(KEY_TYPE o) { throw new UnsupportedOperationException(); } - -#endif - @Override - public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int index = firstIndex; - while(index != -1) { - action.accept(keys[index]); - index = (int)links[index]; - } - } - - @Override - public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int count = 0; - int index = firstIndex; - while(index != -1) { - action.accept(count++, keys[index]); - index = (int)links[index]; - } - } - - @Override - public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int index = firstIndex; - while(index != -1) { - action.accept(input, keys[index]); - index = (int)links[index]; - } - } - - @Override - public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return true; - index = (int)links[index]; - } - return false; - } - - @Override - public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return false; - index = (int)links[index]; - } - return true; - } - - @Override - public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(!filter.test(keys[index])) return false; - index = (int)links[index]; - } - return true; - } - -#if !TYPE_OBJECT - @Override - public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = identity; - int index = firstIndex; - while(index != -1) { - state = operator.APPLY_VALUE(state, keys[index]); - 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_VALUE(state, keys[index]); - index = (int)links[index]; - } - return state; - } - -#endif - @Override - public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = EMPTY_VALUE; - boolean empty = true; - int index = firstIndex; - while(index != -1) { - if(empty) { - state = keys[index]; - empty = false; - } - else state = operator.APPLY_VALUE(state, keys[index]); - index = (int)links[index]; - } - return state; - } - - @Override - public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return keys[index]; - index = (int)links[index]; - } - return EMPTY_VALUE; - } - - @Override - public int count(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int result = 0; - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) result++; - index = (int)links[index]; - } - return result; - } - - @Override - public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { - return new SetIterator(); - } - - @Override - public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { - return new SetIterator(fromElement); - } - - @Override - public IMMUTABLE_HASH_SET KEY_GENERIC_TYPE copy() { - IMMUTABLE_HASH_SET KEY_GENERIC_TYPE set = new IMMUTABLE_HASH_SETBRACES(); - set.containsNull = containsNull; - set.firstIndex = firstIndex; - set.lastIndex = lastIndex; - set.size = size; - set.mask = mask; - set.nullIndex = nullIndex; - set.keys = Arrays.copyOf(keys, keys.length); - set.links = Arrays.copyOf(links, links.length); - return set; - } - - @Override - public void clear() { throw new UnsupportedOperationException(); } - - @Override - public int size() { - return size; - } - - private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { - int previous = -1; - int next = -1; - int current = -1; - int index = 0; - - SetIterator() { - next = firstIndex; - } - - SetIterator(KEY_TYPE from) { - if(KEY_EQUALS_NULL(from)) { - if(containsNull) { - next = (int) links[nullIndex]; - previous = nullIndex; - } - else throw new NoSuchElementException("The null element is not in the set"); - } - else if(KEY_EQUALS(keys[lastIndex], from)) { - previous = lastIndex; - index = size; - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(from)) & mask; - while(KEY_EQUALS_NOT_NULL(keys[pos])) { - if(KEY_EQUALS(keys[pos], from)) { - next = (int)links[pos]; - previous = pos; - break; - } - pos = ++pos & mask; - } - if(previous == -1 && next == -1) - throw new NoSuchElementException("The element was not found"); - } - } - - @Override - public int skip(int amount) { - int result = 0; - while(next != -1 && result != amount) { - current = previous = next; - next = (int)(links[current]); - result++; - } - if(index >= 0) index+=result; - return result; - } - - @Override - public int back(int amount) { - int result = 0; - while(previous != -1 && result != amount) { - current = next = previous; - previous = (int)(links[current] >> 32); - result++; - } - if(index >= 0) index-=result; - return result; - } - - @Override - public boolean hasNext() { - return next != -1; - } - - @Override - public boolean hasPrevious() { - return previous != -1; - } - - @Override - public int nextIndex() { - ensureIndexKnown(); - return index; - } - - @Override - public int previousIndex() { - ensureIndexKnown(); - return index - 1; - } - - @Override - public void remove() { throw new UnsupportedOperationException(); } - - @Override - public KEY_TYPE PREVIOUS() { - if(!hasPrevious()) throw new NoSuchElementException(); - current = next = previous; - previous = (int)(links[current] >> 32); - if(index >= 0) index--; - return keys[current]; - } - - @Override - public KEY_TYPE NEXT() { - if(!hasNext()) throw new NoSuchElementException(); - current = previous = next; - next = (int)(links[current]); - if(index >= 0) index++; - return keys[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++); - } - } - } - -#if TYPE_OBJECT - @Override - public void set(Object e) { throw new UnsupportedOperationException(); } - - @Override - public void add(Object e) { throw new UnsupportedOperationException(); } -#else - @Override - public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } - - @Override - public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } -#endif - } +package speiger.src.collections.PACKAGE.sets; + +#if TYPE_OBJECT +import java.util.function.Consumer; +import java.util.function.BiFunction; +#endif +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +#if JDK_FUNCTION +import java.util.function.PREDICATE; +#endif + +import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; +import speiger.src.collections.PACKAGE.collections.COLLECTION; +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.functions.CONSUMER; +#endif +import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; +import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; +#if !JDK_FUNCTION +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; +import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; +import speiger.src.collections.PACKAGE.utils.ARRAYS; +#if !TYPE_OBJECT +import speiger.src.collections.objects.utils.ObjectArrays; +#endif +import speiger.src.collections.PACKAGE.utils.ITERATORS; + +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.SanityChecks; + +/** + * A Type Specific Custom implementation of the HashSet + * Instead of using Wrapper Object Arrays for storing keys and values there is dedicated arrays for storing keys. + * Extra to that there is a couple quality of life functions provided + * @Type(T) + */ +public class IMMUTABLE_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements ORDERED_SET KEY_GENERIC_TYPE +{ + /** The Backing keys array */ + protected transient KEY_TYPE[] keys; + /** The Backing array for links between nodes. Left 32 Bits => Previous Entry, Right 32 Bits => Next Entry */ + protected transient long[] links; + /** If a null value is present */ + protected transient boolean containsNull; + /** Index of the Null Value */ + protected transient int nullIndex; + /** Max Index that is allowed to be searched through nullIndex - 1 */ + protected transient int mask; + /** Amount of Elements stored in the HashSet */ + protected int size; + /** The First Index in the Map */ + protected int firstIndex = -1; + /** The Last Index in the Map */ + protected int lastIndex = -1; + + /** + * Helper constructor to optimize the copying + * Only accessible through implementations + */ + protected IMMUTABLE_HASH_SET() {} + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + */ + public IMMUTABLE_HASH_SET(KEY_TYPE[] array) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @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 IMMUTABLE_HASH_SET(KEY_TYPE[] array, float loadFactor) { + this(array, 0, array.length, loadFactor); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public IMMUTABLE_HASH_SET(KEY_TYPE[] array, int offset, int length) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @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 + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public IMMUTABLE_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor) { + init(array, offset, length, loadFactor); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + */ + @Primitive + public IMMUTABLE_HASH_SET(Collection collection) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @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 + */ + @Primitive + public IMMUTABLE_HASH_SET(Collection collection, float loadFactor) { +#if !TYPE_OBJECT + init(ARRAYS.unwrap(collection.toArray(NEW_CLASS_ARRAY(collection.size()))), 0, collection.size(), loadFactor); +#else + init(collection.toArray(NEW_CLASS_ARRAY(collection.size())), 0, collection.size(), loadFactor); +#endif + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + */ + public IMMUTABLE_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @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 IMMUTABLE_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor) { + init(collection.TO_ARRAY(NEW_KEY_ARRAY(collection.size())), 0, collection.size(), loadFactor); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + */ + public IMMUTABLE_HASH_SET(Iterator iterator) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @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 IMMUTABLE_HASH_SET(Iterator iterator, float loadFactor) { + this(ITERATORS.wrap(iterator), loadFactor); + } + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + */ + public IMMUTABLE_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @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 IMMUTABLE_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor) { + KEY_TYPE[] array = ARRAYS.pour(iterator); + init(array, 0, array.length, loadFactor); + } + + protected void init(KEY_TYPE[] a, int offset, int length, float loadFactor) { + SanityChecks.checkArrayCapacity(a.length, offset, length); + int newSize = HashUtil.arraySize(length+1, loadFactor); + int newMask = newSize - 1; + KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); + long[] newLinks = new long[newSize + 1]; + int prev = -1; + for(int i = offset,m=offset+length;i c) { throw new UnsupportedOperationException(); } + @Override + public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { 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) { throw new UnsupportedOperationException(); } + @Override + public boolean moveToLast(KEY_TYPE o) { throw new UnsupportedOperationException(); } + + @Override + public boolean contains(Object o) { +#if TYPE_OBJECT + if(o == null) return containsNull; +#else + if(o == null) return false; + if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return containsNull; +#endif + int pos = HashUtil.mix(o.hashCode()) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(EQUALS_KEY_TYPE(current, o)) return true; + while(true) { + if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; + else if(EQUALS_KEY_TYPE(current, o)) return true; + } + } + + @Override + public KEY_TYPE FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex]; + } + + @Override + public KEY_TYPE POLL_FIRST_KEY() { throw new UnsupportedOperationException(); } + + @Override + public KEY_TYPE LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex]; + } + + @Override + public KEY_TYPE POLL_LAST_KEY() { throw new UnsupportedOperationException(); } + @Override + public boolean remove(Object o) { throw new UnsupportedOperationException(); } + +#if !TYPE_OBJECT + @Override + public boolean contains(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) return containsNull; + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(KEY_EQUALS(current, o)) return true; + while(true) { + if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; + else if(KEY_EQUALS(current, o)) return true; + } + } + + @Override + public boolean remove(KEY_TYPE o) { throw new UnsupportedOperationException(); } + +#endif + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1) { + action.accept(keys[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int count = 0; + int index = firstIndex; + while(index != -1) { + action.accept(count++, keys[index]); + index = (int)links[index]; + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1) { + action.accept(input, keys[index]); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(!filter.test(keys[index])) return false; + index = (int)links[index]; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.APPLY_VALUE(state, keys[index]); + 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_VALUE(state, keys[index]); + index = (int)links[index]; + } + return state; + } + +#endif + @Override + public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_VALUE; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + state = keys[index]; + empty = false; + } + else state = operator.APPLY_VALUE(state, keys[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return keys[index]; + index = (int)links[index]; + } + return EMPTY_VALUE; + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int result = 0; + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) result++; + index = (int)links[index]; + } + return result; + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + return new SetIterator(fromElement); + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { + if(a == null || a.length < size()) a = new KEY_TYPE[size()]; + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + a[i] = keys[index]; + } + if (a.length > size) a[size] = EMPTY_KEY_VALUE; + return a; + } + +#endif + @Override + @Deprecated + public Object[] toArray() { + if(isEmpty()) return ObjectArrays.EMPTY_ARRAY; + Object[] obj = new Object[size()]; + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + obj[i] = KEY_TO_OBJ(keys[index]); + } + return obj; + } + + @Override + @Primitive + public E[] toArray(E[] a) { + if(a == null) a = (E[])new Object[size()]; + else if(a.length < size()) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size()); + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + a[i] = (E)KEY_TO_OBJ(keys[index]); + } + if (a.length > size) a[size] = null; + return a; + } + + @Override + public IMMUTABLE_HASH_SET KEY_GENERIC_TYPE copy() { + IMMUTABLE_HASH_SET KEY_GENERIC_TYPE set = new IMMUTABLE_HASH_SETBRACES(); + set.containsNull = containsNull; + set.firstIndex = firstIndex; + set.lastIndex = lastIndex; + set.size = size; + set.mask = mask; + set.nullIndex = nullIndex; + set.keys = Arrays.copyOf(keys, keys.length); + set.links = Arrays.copyOf(links, links.length); + return set; + } + + @Override + public void clear() { throw new UnsupportedOperationException(); } + + @Override + public int size() { + return size; + } + + private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { + int previous = -1; + int next = -1; + int current = -1; + int index = 0; + + SetIterator() { + next = firstIndex; + } + + SetIterator(KEY_TYPE from) { + if(KEY_EQUALS_NULL(from)) { + if(containsNull) { + next = (int) links[nullIndex]; + previous = nullIndex; + } + else throw new NoSuchElementException("The null element is not in the set"); + } + else if(KEY_EQUALS(keys[lastIndex], from)) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(from)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos], from)) { + next = (int)links[pos]; + previous = pos; + break; + } + pos = ++pos & mask; + } + if(previous == -1 && next == -1) + throw new NoSuchElementException("The element was not found"); + } + } + + @Override + public int skip(int amount) { + int result = 0; + while(next != -1 && result != amount) { + current = previous = next; + next = (int)(links[current]); + result++; + } + if(index >= 0) index+=result; + return result; + } + + @Override + public int back(int amount) { + int result = 0; + while(previous != -1 && result != amount) { + current = next = previous; + previous = (int)(links[current] >> 32); + result++; + } + if(index >= 0) index-=result; + return result; + } + + @Override + public boolean hasNext() { + return next != -1; + } + + @Override + public boolean hasPrevious() { + return previous != -1; + } + + @Override + public int nextIndex() { + ensureIndexKnown(); + return index; + } + + @Override + public int previousIndex() { + ensureIndexKnown(); + return index - 1; + } + + @Override + public void remove() { throw new UnsupportedOperationException(); } + + @Override + public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); + current = next = previous; + previous = (int)(links[current] >> 32); + if(index >= 0) index--; + return keys[current]; + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + current = previous = next; + next = (int)(links[current]); + if(index >= 0) index++; + return keys[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++); + } + } + } + +#if TYPE_OBJECT + @Override + public void set(Object e) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object e) { throw new UnsupportedOperationException(); } +#else + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } +#endif + } } \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template index d638c90..ba5282f 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenCustomHashSet.template @@ -1,861 +1,897 @@ -package speiger.src.collections.PACKAGE.sets; - -#if TYPE_OBJECT -import java.util.function.Consumer; -import java.util.function.BiFunction; -#endif -import java.util.Arrays; -import java.util.Objects; -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; -#if JDK_FUNCTION -import java.util.function.PREDICATE; -#endif - -import speiger.src.collections.PACKAGE.collections.COLLECTION; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.collections.ITERATOR; -#endif -import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; -import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.utils.ITERATORS; -import speiger.src.collections.PACKAGE.functions.CONSUMER; -#endif -import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; -import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; -#if !JDK_FUNCTION -import speiger.src.collections.PACKAGE.functions.function.PREDICATE; -#endif -import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; -import speiger.src.collections.PACKAGE.utils.STRATEGY; -import speiger.src.collections.utils.HashUtil; -import speiger.src.collections.utils.SanityChecks; - -/** - * A Type Specific LinkedHashSet that allows for custom HashControl. That uses arrays to create links between nodes. - * For cases where Objects/primitive do not allow hashcoding this can be really useful and provide a lot of control. - * This implementation of SortedSet does not support SubSet of any kind. It implements the interface due to sortability and first/last access - * @Type(T) - */ -public class LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE extends CUSTOM_HASH_SET KEY_GENERIC_TYPE implements ORDERED_SET KEY_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 Contstructor - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public LINKED_CUSTOM_HASH_SET(STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Constructor that defines the minimum capacity - * @param minCapacity the minimum capacity the HashSet is allowed to be. - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the minimum capacity is negative - */ - public LINKED_CUSTOM_HASH_SET(int minCapacity, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Constructor that defines the minimum capacity and load factor - * @param minCapacity the minimum capacity the HashSet is allowed to be. - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @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_CUSTOM_HASH_SET(int minCapacity, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - super(minCapacity, loadFactor, strategy); - links = new long[nullIndex + 1]; - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(array, 0, array.length, loadFactor, strategy); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(length, strategy); - SanityChecks.checkArrayCapacity(array.length, offset, length); - for(int i = 0;i collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - @Primitive - public LINKED_CUSTOM_HASH_SET(Collection collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection.size(), loadFactor, strategy); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public LINKED_CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public LINKED_CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection.size(), strategy); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public LINKED_CUSTOM_HASH_SET(Iterator iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public LINKED_CUSTOM_HASH_SET(Iterator iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { -#if !TYPE_OBJECT - this(ITERATORS.wrap(iterator), loadFactor, strategy); -#else - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); - while(iterator.hasNext()) add(iterator.next()); -#endif - } - -#if !TYPE_OBJECT - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public LINKED_CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public LINKED_CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); - while(iterator.hasNext()) add(iterator.NEXT()); - } - -#endif - @Override - public boolean addAndMoveToFirst(KEY_TYPE o) { - if(strategy.equals(o, EMPTY_KEY_VALUE)) { - if(containsNull) { - moveToFirstIndex(nullIndex); - return false; - } - containsNull = true; - onNodeAdded(nullIndex); - moveToFirstIndex(nullIndex); - } - else { - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { - if(strategy.equals(keys[pos], o)) { - moveToFirstIndex(pos); - return false; - } - pos = ++pos & mask; - } - keys[pos] = o; - onNodeAdded(pos); - moveToFirstIndex(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return true; - } - - @Override - public boolean addAndMoveToLast(KEY_TYPE o) { - if(strategy.equals(o, EMPTY_KEY_VALUE)) { - if(containsNull) { - moveToLastIndex(nullIndex); - return false; - } - containsNull = true; - onNodeAdded(nullIndex); - } - else { - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { - if(strategy.equals(keys[pos], o)) { - moveToLastIndex(pos); - return false; - } - pos = ++pos & mask; - } - keys[pos] = o; - onNodeAdded(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return true; - } - - @Override - public boolean moveToFirst(KEY_TYPE o) { - if(isEmpty() || strategy.equals(FIRST_KEY(), o)) return false; - if(strategy.equals(o, EMPTY_KEY_VALUE)) { - if(containsNull) { - moveToFirstIndex(nullIndex); - return true; - } - } - else { - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { - if(strategy.equals(keys[pos], o)) { - moveToFirstIndex(pos); - return true; - } - pos = ++pos & mask; - } - } - return false; - } - - @Override - public boolean moveToLast(KEY_TYPE o) { - if(isEmpty() || strategy.equals(LAST_KEY(), o)) return false; - if(strategy.equals(o, EMPTY_KEY_VALUE)) { - if(containsNull) { - moveToLastIndex(nullIndex); - return true; - } - } - else { - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { - if(strategy.equals(keys[pos], o)) { - moveToLastIndex(pos); - return true; - } - pos = ++pos & mask; - } - } - return false; - } - - protected void moveToFirstIndex(int startPos) { - if(size == 1 || firstIndex == startPos) return; - if(lastIndex == startPos) { - lastIndex = (int)(links[startPos] >>> 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) { - if(size == 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 - public KEY_TYPE FIRST_KEY() { - if(size == 0) throw new NoSuchElementException(); - return keys[firstIndex]; - } - - @Override - public KEY_TYPE POLL_FIRST_KEY() { - if(size == 0) throw new NoSuchElementException(); - int pos = firstIndex; - onNodeRemoved(pos); - KEY_TYPE result = keys[pos]; - size--; - if(strategy.equals(result, EMPTY_KEY_VALUE)) { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else shiftKeys(pos); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return result; - } - - @Override - public KEY_TYPE LAST_KEY() { - if(size == 0) throw new NoSuchElementException(); - return keys[lastIndex]; - } - - @Override - public KEY_TYPE POLL_LAST_KEY() { - if(size == 0) throw new NoSuchElementException(); - int pos = lastIndex; - onNodeRemoved(pos); - KEY_TYPE result = keys[pos]; - size--; - if(strategy.equals(result, EMPTY_KEY_VALUE)) { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else shiftKeys(pos); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return result; - } - - @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; - KEY_TYPE[] newKeys = NEW_KEY_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(strategy.equals(keys[i], EMPTY_KEY_VALUE)) pos = newSize; - else { - pos = HashUtil.mix(strategy.hashCode(keys[i])) & newMask; - while(!strategy.equals(newKeys[pos], EMPTY_KEY_VALUE)) pos = ++pos & newMask; - } - newKeys[pos] = keys[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; - nullIndex = newSize; - mask = newMask; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = newKeys; - } - - @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 >= nullIndex) { - clear(); - return; - } - nullIndex = request; - mask = request-1; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = NEW_KEY_ARRAY(request + 1); - links = new long[request + 1]; - firstIndex = lastIndex = -1; - this.size = 0; - containsNull = false; - } - - @Override - public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int index = firstIndex; - while(index != -1){ - action.accept(keys[index]); - index = (int)links[index]; - } - } - - @Override - public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int count = 0; - int index = firstIndex; - while(index != -1) { - action.accept(count++, keys[index]); - index = (int)links[index]; - } - } - - @Override - public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int index = firstIndex; - while(index != -1) { - action.accept(input, keys[index]); - index = (int)links[index]; - } - } - - @Override - public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return true; - index = (int)links[index]; - } - return false; - } - - @Override - public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return false; - index = (int)links[index]; - } - return true; - } - - @Override - public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(!filter.test(keys[index])) return false; - index = (int)links[index]; - } - return true; - } - -#if !TYPE_OBJECT - @Override - public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = identity; - int index = firstIndex; - while(index != -1) { - state = operator.APPLY_VALUE(state, keys[index]); - 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_VALUE(state, keys[index]); - index = (int)links[index]; - } - return state; - } - -#endif - @Override - public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = EMPTY_VALUE; - boolean empty = true; - int index = firstIndex; - while(index != -1) { - if(empty) { - state = keys[index]; - empty = false; - } - else state = operator.APPLY_VALUE(state, keys[index]); - index = (int)links[index]; - } - return state; - } - - @Override - public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return keys[index]; - index = (int)links[index]; - } - return EMPTY_VALUE; - } - - @Override - public int count(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int result = 0; - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) result++; - index = (int)links[index]; - } - return result; - } - - @Override - public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { - return new SetIterator(); - } - - @Override - public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { - return new SetIterator(fromElement); - } - - @Override - public LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE copy() { - LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE set = new LINKED_CUSTOM_HASH_SETBRACES(0, loadFactor, strategy); - set.minCapacity = minCapacity; - set.mask = mask; - set.maxFill = maxFill; - set.nullIndex = nullIndex; - set.containsNull = containsNull; - set.size = size; - set.keys = Arrays.copyOf(keys, keys.length); - set.links = Arrays.copyOf(links, links.length); - set.firstIndex = firstIndex; - set.lastIndex = lastIndex; - return set; - } - - private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { - int previous = -1; - int next = -1; - int current = -1; - int index = 0; - - SetIterator() { - next = firstIndex; - } - - SetIterator(KEY_TYPE from) { - if(strategy.equals(from, EMPTY_KEY_VALUE)) { - if(containsNull) { - next = (int) links[nullIndex]; - previous = nullIndex; - } - else throw new NoSuchElementException("The null element is not in the set"); - } - else if(strategy.equals(keys[lastIndex], from)) { - previous = lastIndex; - index = size; - } - else { - int pos = HashUtil.mix(strategy.hashCode(from)) & mask; - while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { - if(strategy.equals(keys[pos], from)) { - next = (int)links[pos]; - previous = pos; - break; - } - pos = ++pos & mask; - } - if(previous == -1 && next == -1) - throw new NoSuchElementException("The element was not found"); - } - } - - @Override - public boolean hasNext() { - return next != -1; - } - - @Override - public boolean hasPrevious() { - return previous != -1; - } - - @Override - public int skip(int amount) { - int result = 0; - while(next != -1 && result != amount) { - current = previous = next; - next = (int)(links[current]); - result++; - } - if(index >= 0) index+=result; - return result; - } - - @Override - public int back(int amount) { - int result = 0; - while(previous != -1 && result != amount) { - current = next = previous; - previous = (int)(links[current] >> 32); - result++; - } - if(index >= 0) index-=result; - return result; - } - - @Override - public int nextIndex() { - ensureIndexKnown(); - return index; - } - - @Override - public int previousIndex() { - ensureIndexKnown(); - return index - 1; - } - - @Override - 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 == nullIndex) { - current = -1; - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else { - int slot, last, startPos = current; - current = -1; - KEY_TYPE current; - while(true) { - startPos = ((last = startPos) + 1) & mask; - while(true){ - if(strategy.equals((current = keys[startPos]), EMPTY_KEY_VALUE)) { - keys[last] = EMPTY_KEY_VALUE; - return; - } - slot = HashUtil.mix(strategy.hashCode(current)) & mask; - if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; - startPos = ++startPos & mask; - } - keys[last] = current; - if(next == startPos) next = last; - if(previous == startPos) previous = last; - onNodeMoved(startPos, last); - } - } - } - - @Override - public KEY_TYPE PREVIOUS() { - if(!hasPrevious()) throw new NoSuchElementException(); - current = next = previous; - previous = (int)(links[current] >> 32); - if(index >= 0) index--; - return keys[current]; - } - - @Override - public KEY_TYPE NEXT() { - if(!hasNext()) throw new NoSuchElementException(); - current = previous = next; - next = (int)(links[current]); - if(index >= 0) index++; - return keys[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++); - } - } - } - -#if TYPE_OBJECT - @Override - public void set(Object e) { throw new UnsupportedOperationException(); } - - @Override - public void add(Object e) { throw new UnsupportedOperationException(); } -#else - @Override - public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } - - @Override - public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } -#endif - } -} +package speiger.src.collections.PACKAGE.sets; + +#if TYPE_OBJECT +import java.util.function.Consumer; +import java.util.function.BiFunction; +#endif +import java.util.Arrays; +import java.util.Objects; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +#if JDK_FUNCTION +import java.util.function.PREDICATE; +#endif + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#endif +import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; +import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.utils.ITERATORS; +import speiger.src.collections.PACKAGE.functions.CONSUMER; +#endif +import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; +import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; +#if !JDK_FUNCTION +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +import speiger.src.collections.objects.utils.ObjectArrays; +import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; +import speiger.src.collections.PACKAGE.utils.STRATEGY; +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.SanityChecks; + +/** + * A Type Specific LinkedHashSet that allows for custom HashControl. That uses arrays to create links between nodes. + * For cases where Objects/primitive do not allow hashcoding this can be really useful and provide a lot of control. + * This implementation of SortedSet does not support SubSet of any kind. It implements the interface due to sortability and first/last access + * @Type(T) + */ +public class LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE extends CUSTOM_HASH_SET KEY_GENERIC_TYPE implements ORDERED_SET KEY_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 Contstructor + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public LINKED_CUSTOM_HASH_SET(STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Constructor that defines the minimum capacity + * @param minCapacity the minimum capacity the HashSet is allowed to be. + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the minimum capacity is negative + */ + public LINKED_CUSTOM_HASH_SET(int minCapacity, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Constructor that defines the minimum capacity and load factor + * @param minCapacity the minimum capacity the HashSet is allowed to be. + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @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_CUSTOM_HASH_SET(int minCapacity, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + super(minCapacity, loadFactor, strategy); + links = new long[nullIndex + 1]; + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, loadFactor, strategy); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public LINKED_CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(length, strategy); + SanityChecks.checkArrayCapacity(array.length, offset, length); + for(int i = 0;i collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + @Primitive + public LINKED_CUSTOM_HASH_SET(Collection collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), loadFactor, strategy); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public LINKED_CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public LINKED_CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), strategy); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public LINKED_CUSTOM_HASH_SET(Iterator iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public LINKED_CUSTOM_HASH_SET(Iterator iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { +#if !TYPE_OBJECT + this(ITERATORS.wrap(iterator), loadFactor, strategy); +#else + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.next()); +#endif + } + +#if !TYPE_OBJECT + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public LINKED_CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public LINKED_CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.NEXT()); + } + +#endif + @Override + public boolean addAndMoveToFirst(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_KEY_VALUE)) { + if(containsNull) { + moveToFirstIndex(nullIndex); + return false; + } + containsNull = true; + onNodeAdded(nullIndex); + moveToFirstIndex(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToFirstIndex(pos); + return false; + } + pos = ++pos & mask; + } + keys[pos] = o; + onNodeAdded(pos); + moveToFirstIndex(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + public boolean addAndMoveToLast(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_KEY_VALUE)) { + if(containsNull) { + moveToLastIndex(nullIndex); + return false; + } + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToLastIndex(pos); + return false; + } + pos = ++pos & mask; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + public boolean moveToFirst(KEY_TYPE o) { + if(isEmpty() || strategy.equals(FIRST_KEY(), o)) return false; + if(strategy.equals(o, EMPTY_KEY_VALUE)) { + if(containsNull) { + moveToFirstIndex(nullIndex); + return true; + } + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToFirstIndex(pos); + return true; + } + pos = ++pos & mask; + } + } + return false; + } + + @Override + public boolean moveToLast(KEY_TYPE o) { + if(isEmpty() || strategy.equals(LAST_KEY(), o)) return false; + if(strategy.equals(o, EMPTY_KEY_VALUE)) { + if(containsNull) { + moveToLastIndex(nullIndex); + return true; + } + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { + if(strategy.equals(keys[pos], o)) { + moveToLastIndex(pos); + return true; + } + pos = ++pos & mask; + } + } + return false; + } + + protected void moveToFirstIndex(int startPos) { + if(size == 1 || firstIndex == startPos) return; + if(lastIndex == startPos) { + lastIndex = (int)(links[startPos] >>> 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) { + if(size == 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 + public KEY_TYPE FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex]; + } + + @Override + public KEY_TYPE POLL_FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + KEY_TYPE result = keys[pos]; + size--; + if(strategy.equals(result, EMPTY_KEY_VALUE)) { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return result; + } + + @Override + public KEY_TYPE LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex]; + } + + @Override + public KEY_TYPE POLL_LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + KEY_TYPE result = keys[pos]; + size--; + if(strategy.equals(result, EMPTY_KEY_VALUE)) { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return result; + } + + @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; + KEY_TYPE[] newKeys = NEW_KEY_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(strategy.equals(keys[i], EMPTY_KEY_VALUE)) pos = newSize; + else { + pos = HashUtil.mix(strategy.hashCode(keys[i])) & newMask; + while(!strategy.equals(newKeys[pos], EMPTY_KEY_VALUE)) pos = ++pos & newMask; + } + newKeys[pos] = keys[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; + nullIndex = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = newKeys; + } + + @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 >= nullIndex) { + clear(); + return; + } + nullIndex = request; + mask = request-1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = NEW_KEY_ARRAY(request + 1); + links = new long[request + 1]; + firstIndex = lastIndex = -1; + this.size = 0; + containsNull = false; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { + if(a == null || a.length < size()) a = new KEY_TYPE[size()]; + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + a[i] = keys[index]; + } + if (a.length > size) a[size] = EMPTY_KEY_VALUE; + return a; + } + +#endif + @Override + @Deprecated + public Object[] toArray() { + if(isEmpty()) return ObjectArrays.EMPTY_ARRAY; + Object[] obj = new Object[size()]; + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + obj[i] = KEY_TO_OBJ(keys[index]); + } + return obj; + } + + @Override + @Primitive + public E[] toArray(E[] a) { + if(a == null) a = (E[])new Object[size()]; + else if(a.length < size()) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size()); + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + a[i] = (E)KEY_TO_OBJ(keys[index]); + } + if (a.length > size) a[size] = null; + return a; + } + + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(keys[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int count = 0; + int index = firstIndex; + while(index != -1) { + action.accept(count++, keys[index]); + index = (int)links[index]; + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1) { + action.accept(input, keys[index]); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(!filter.test(keys[index])) return false; + index = (int)links[index]; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.APPLY_VALUE(state, keys[index]); + 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_VALUE(state, keys[index]); + index = (int)links[index]; + } + return state; + } + +#endif + @Override + public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_VALUE; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + state = keys[index]; + empty = false; + } + else state = operator.APPLY_VALUE(state, keys[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return keys[index]; + index = (int)links[index]; + } + return EMPTY_VALUE; + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int result = 0; + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) result++; + index = (int)links[index]; + } + return result; + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + return new SetIterator(fromElement); + } + + @Override + public LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE copy() { + LINKED_CUSTOM_HASH_SET KEY_GENERIC_TYPE set = new LINKED_CUSTOM_HASH_SETBRACES(0, loadFactor, strategy); + set.minCapacity = minCapacity; + set.mask = mask; + set.maxFill = maxFill; + set.nullIndex = nullIndex; + set.containsNull = containsNull; + set.size = size; + set.keys = Arrays.copyOf(keys, keys.length); + set.links = Arrays.copyOf(links, links.length); + set.firstIndex = firstIndex; + set.lastIndex = lastIndex; + return set; + } + + private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { + int previous = -1; + int next = -1; + int current = -1; + int index = 0; + + SetIterator() { + next = firstIndex; + } + + SetIterator(KEY_TYPE from) { + if(strategy.equals(from, EMPTY_KEY_VALUE)) { + if(containsNull) { + next = (int) links[nullIndex]; + previous = nullIndex; + } + else throw new NoSuchElementException("The null element is not in the set"); + } + else if(strategy.equals(keys[lastIndex], from)) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(strategy.hashCode(from)) & mask; + while(!strategy.equals(keys[pos], EMPTY_KEY_VALUE)) { + if(strategy.equals(keys[pos], from)) { + next = (int)links[pos]; + previous = pos; + break; + } + pos = ++pos & mask; + } + if(previous == -1 && next == -1) + throw new NoSuchElementException("The element was not found"); + } + } + + @Override + public boolean hasNext() { + return next != -1; + } + + @Override + public boolean hasPrevious() { + return previous != -1; + } + + @Override + public int skip(int amount) { + int result = 0; + while(next != -1 && result != amount) { + current = previous = next; + next = (int)(links[current]); + result++; + } + if(index >= 0) index+=result; + return result; + } + + @Override + public int back(int amount) { + int result = 0; + while(previous != -1 && result != amount) { + current = next = previous; + previous = (int)(links[current] >> 32); + result++; + } + if(index >= 0) index-=result; + return result; + } + + @Override + public int nextIndex() { + ensureIndexKnown(); + return index; + } + + @Override + public int previousIndex() { + ensureIndexKnown(); + return index - 1; + } + + @Override + 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 == nullIndex) { + current = -1; + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else { + int slot, last, startPos = current; + current = -1; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(strategy.equals((current = keys[startPos]), EMPTY_KEY_VALUE)) { + keys[last] = EMPTY_KEY_VALUE; + return; + } + slot = HashUtil.mix(strategy.hashCode(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current; + if(next == startPos) next = last; + if(previous == startPos) previous = last; + onNodeMoved(startPos, last); + } + } + } + + @Override + public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); + current = next = previous; + previous = (int)(links[current] >> 32); + if(index >= 0) index--; + return keys[current]; + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + current = previous = next; + next = (int)(links[current]); + if(index >= 0) index++; + return keys[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++); + } + } + } + +#if TYPE_OBJECT + @Override + public void set(Object e) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object e) { throw new UnsupportedOperationException(); } +#else + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } +#endif + } +} diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template index 3e7818b..9ebffa8 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/LinkedOpenHashSet.template @@ -1,832 +1,868 @@ -package speiger.src.collections.PACKAGE.sets; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; -#if TYPE_OBJECT -import java.util.function.Consumer; -import java.util.function.BiFunction; -#endif -#if JDK_FUNCTION -import java.util.function.PREDICATE; -#endif - -import speiger.src.collections.PACKAGE.collections.COLLECTION; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.collections.ITERATOR; -#endif -import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.functions.CONSUMER; -#endif -import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; -import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; -#if !JDK_FUNCTION -import speiger.src.collections.PACKAGE.functions.function.PREDICATE; -#endif -import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; -import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.utils.ITERATORS; -#endif -import speiger.src.collections.utils.HashUtil; -import speiger.src.collections.utils.SanityChecks; - -/** - * 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 SortedSet does not support SubSet of any kind. It implements the interface due to sortability and first/last access - * @Type(T) - */ -public class LINKED_HASH_SET KEY_GENERIC_TYPE extends HASH_SET KEY_GENERIC_TYPE implements ORDERED_SET KEY_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_HASH_SET() { - this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Constructor that defines the minimum capacity - * @param minCapacity the minimum capacity the HashSet is allowed to be. - * @throws IllegalStateException if the minimum capacity is negative - */ - public LINKED_HASH_SET(int minCapacity) { - this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Constructor that defines the minimum capacity and load factor - * @param minCapacity the minimum capacity the HashSet 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_HASH_SET(int minCapacity, float loadFactor) { - super(minCapacity, loadFactor); - links = new long[nullIndex + 1]; - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - */ - public LINKED_HASH_SET(KEY_TYPE[] array) { - this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @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_HASH_SET(KEY_TYPE[] array, float loadFactor) { - this(array, 0, array.length, loadFactor); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public LINKED_HASH_SET(KEY_TYPE[] array, int offset, int length) { - this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @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 - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public LINKED_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor) { - this(length); - SanityChecks.checkArrayCapacity(array.length, offset, length); - for(int i = 0;i collection) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @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 - */ - @Primitive - public LINKED_HASH_SET(Collection collection, float loadFactor) { - this(collection.size(), loadFactor); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - */ - public LINKED_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @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_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor) { - this(collection.size()); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - */ - public LINKED_HASH_SET(Iterator iterator) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @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_HASH_SET(Iterator iterator, float loadFactor) { -#if !TYPE_OBJECT - this(ITERATORS.wrap(iterator), loadFactor); -#else - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); - while(iterator.hasNext()) add(iterator.next()); -#endif - } - -#if !TYPE_OBJECT - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - */ - public LINKED_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @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_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor) { - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); - while(iterator.hasNext()) add(iterator.NEXT()); - } - -#endif - @Override - public boolean addAndMoveToFirst(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) { - if(containsNull) { - moveToFirstIndex(nullIndex); - return false; - } - containsNull = true; - onNodeAdded(nullIndex); - moveToFirstIndex(nullIndex); - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - while(KEY_EQUALS_NOT_NULL(keys[pos])) { - if(KEY_EQUALS(keys[pos], o)) { - moveToFirstIndex(pos); - return false; - } - pos = ++pos & mask; - } - keys[pos] = o; - onNodeAdded(pos); - moveToFirstIndex(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return true; - } - - @Override - public boolean addAndMoveToLast(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) { - if(containsNull) { - moveToLastIndex(nullIndex); - return false; - } - containsNull = true; - onNodeAdded(nullIndex); - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - while(KEY_EQUALS_NOT_NULL(keys[pos])) { - if(KEY_EQUALS(keys[pos], o)) { - moveToLastIndex(pos); - return false; - } - pos = ++pos & mask; - } - keys[pos] = o; - onNodeAdded(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return true; - } - - @Override - public boolean moveToFirst(KEY_TYPE o) { - if(isEmpty() || KEY_EQUALS(FIRST_KEY(), o)) return false; - if(KEY_EQUALS_NULL(o)) { - if(containsNull) { - moveToFirstIndex(nullIndex); - return true; - } - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - while(KEY_EQUALS_NOT_NULL(keys[pos])) { - if(KEY_EQUALS(keys[pos], o)) { - moveToFirstIndex(pos); - return true; - } - pos = ++pos & mask; - } - } - return false; - } - - @Override - public boolean moveToLast(KEY_TYPE o) { - if(isEmpty() || KEY_EQUALS(LAST_KEY(), o)) return false; - if(KEY_EQUALS_NULL(o)) { - if(containsNull) { - moveToLastIndex(nullIndex); - return true; - } - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - while(KEY_EQUALS_NOT_NULL(keys[pos])) { - if(KEY_EQUALS(keys[pos], o)) { - moveToLastIndex(pos); - return true; - } - pos = ++pos & mask; - } - } - return false; - } - - protected void moveToFirstIndex(int startPos) { - if(size == 1 || firstIndex == startPos) return; - if(lastIndex == startPos) { - lastIndex = (int)(links[startPos] >>> 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) { - if(size == 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 - public KEY_TYPE FIRST_KEY() { - if(size == 0) throw new NoSuchElementException(); - return keys[firstIndex]; - } - - @Override - public KEY_TYPE POLL_FIRST_KEY() { - if(size == 0) throw new NoSuchElementException(); - int pos = firstIndex; - onNodeRemoved(pos); - KEY_TYPE result = keys[pos]; - size--; - if(KEY_EQUALS_NULL(result)) { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else shiftKeys(pos); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return result; - } - - @Override - public KEY_TYPE LAST_KEY() { - if(size == 0) throw new NoSuchElementException(); - return keys[lastIndex]; - } - - @Override - public KEY_TYPE POLL_LAST_KEY() { - if(size == 0) throw new NoSuchElementException(); - int pos = lastIndex; - onNodeRemoved(pos); - KEY_TYPE result = keys[pos]; - size--; - if(KEY_EQUALS_NULL(result)) { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else shiftKeys(pos); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return result; - } - - @Override - public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int index = firstIndex; - while(index != -1){ - action.accept(keys[index]); - index = (int)links[index]; - } - } - - @Override - public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int count = 0; - int index = firstIndex; - while(index != -1) { - action.accept(count++, keys[index]); - index = (int)links[index]; - } - } - - @Override - public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { - Objects.requireNonNull(action); - int index = firstIndex; - while(index != -1) { - action.accept(input, keys[index]); - index = (int)links[index]; - } - } - - @Override - public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return true; - index = (int)links[index]; - } - return false; - } - - @Override - public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return false; - index = (int)links[index]; - } - return true; - } - - @Override - public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(!filter.test(keys[index])) return false; - index = (int)links[index]; - } - return true; - } - -#if !TYPE_OBJECT - @Override - public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = identity; - int index = firstIndex; - while(index != -1) { - state = operator.APPLY_VALUE(state, keys[index]); - 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_VALUE(state, keys[index]); - index = (int)links[index]; - } - return state; - } - -#endif - @Override - public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = EMPTY_VALUE; - boolean empty = true; - int index = firstIndex; - while(index != -1) { - if(empty) { - state = keys[index]; - empty = false; - } - else state = operator.APPLY_VALUE(state, keys[index]); - index = (int)links[index]; - } - return state; - } - - @Override - public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) return keys[index]; - index = (int)links[index]; - } - return EMPTY_VALUE; - } - - @Override - public int count(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - int result = 0; - int index = firstIndex; - while(index != -1) { - if(filter.test(keys[index])) result++; - index = (int)links[index]; - } - return result; - } - - @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; - KEY_TYPE[] newKeys = NEW_KEY_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(KEY_TO_HASH(keys[i])) & newMask; - while(KEY_EQUALS_NOT_NULL(newKeys[pos])) pos = ++pos & newMask; - } - newKeys[pos] = keys[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; - nullIndex = newSize; - mask = newMask; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = newKeys; - } - - @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 >= nullIndex) { - clear(); - return; - } - nullIndex = request; - mask = request-1; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = NEW_KEY_ARRAY(request + 1); - links = new long[request + 1]; - firstIndex = lastIndex = -1; - this.size = 0; - containsNull = false; - } - - @Override - public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { - return new SetIterator(); - } - - @Override - public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { - return new SetIterator(fromElement); - } - - @Override - public LINKED_HASH_SET KEY_GENERIC_TYPE copy() { - LINKED_HASH_SET KEY_GENERIC_TYPE set = new LINKED_HASH_SETBRACES(0, loadFactor); - set.minCapacity = minCapacity; - set.mask = mask; - set.maxFill = maxFill; - set.nullIndex = nullIndex; - set.containsNull = containsNull; - set.size = size; - set.keys = Arrays.copyOf(keys, keys.length); - set.links = Arrays.copyOf(links, links.length); - set.firstIndex = firstIndex; - set.lastIndex = lastIndex; - return set; - } - - private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { - int previous = -1; - int next = -1; - int current = -1; - int index = 0; - - SetIterator() { - next = firstIndex; - } - - SetIterator(KEY_TYPE from) { - if(KEY_EQUALS_NULL(from)) { - if(containsNull) { - next = (int) links[nullIndex]; - previous = nullIndex; - } - else throw new NoSuchElementException("The null element is not in the set"); - } - else if(KEY_EQUALS(keys[lastIndex], from)) { - previous = lastIndex; - index = size; - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(from)) & mask; - while(KEY_EQUALS_NOT_NULL(keys[pos])) { - if(KEY_EQUALS(keys[pos], from)) { - next = (int)links[pos]; - previous = pos; - break; - } - pos = ++pos & mask; - } - if(previous == -1 && next == -1) - throw new NoSuchElementException("The element was not found"); - } - } - - @Override - public int skip(int amount) { - int result = 0; - while(next != -1 && result != amount) { - current = previous = next; - next = (int)(links[current]); - result++; - } - if(index >= 0) index+=result; - return result; - } - - @Override - public int back(int amount) { - int result = 0; - while(previous != -1 && result != amount) { - current = next = previous; - previous = (int)(links[current] >> 32); - result++; - } - if(index >= 0) index-=result; - return result; - } - - @Override - public boolean hasNext() { - return next != -1; - } - - @Override - public boolean hasPrevious() { - return previous != -1; - } - - @Override - public int nextIndex() { - ensureIndexKnown(); - return index; - } - - @Override - public int previousIndex() { - ensureIndexKnown(); - return index - 1; - } - - @Override - 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 == nullIndex) { - current = -1; - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else { - int slot, last, startPos = current; - current = -1; - KEY_TYPE current; - while(true) { - startPos = ((last = startPos) + 1) & mask; - while(true){ - if(KEY_EQUALS_NULL((current = keys[startPos]))) { - keys[last] = EMPTY_KEY_VALUE; - return; - } - slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; - if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; - startPos = ++startPos & mask; - } - keys[last] = current; - if(next == startPos) next = last; - if(previous == startPos) previous = last; - onNodeMoved(startPos, last); - } - } - } - - @Override - public KEY_TYPE PREVIOUS() { - if(!hasPrevious()) throw new NoSuchElementException(); - current = next = previous; - previous = (int)(links[current] >> 32); - if(index >= 0) index--; - return keys[current]; - } - - @Override - public KEY_TYPE NEXT() { - if(!hasNext()) throw new NoSuchElementException(); - current = previous = next; - next = (int)(links[current]); - if(index >= 0) index++; - return keys[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++); - } - } - } - -#if TYPE_OBJECT - @Override - public void set(Object e) { throw new UnsupportedOperationException(); } - - @Override - public void add(Object e) { throw new UnsupportedOperationException(); } -#else - @Override - public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } - - @Override - public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } -#endif - } -} +package speiger.src.collections.PACKAGE.sets; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +#if TYPE_OBJECT +import java.util.function.Consumer; +import java.util.function.BiFunction; +#endif +#if JDK_FUNCTION +import java.util.function.PREDICATE; +#endif + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#endif +import speiger.src.collections.PACKAGE.collections.BI_ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.functions.CONSUMER; +#endif +import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; +import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; +#if !JDK_FUNCTION +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; +import speiger.src.collections.PACKAGE.lists.LIST_ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.utils.ITERATORS; +#endif +import speiger.src.collections.objects.utils.ObjectArrays; +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.SanityChecks; + +/** + * 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 SortedSet does not support SubSet of any kind. It implements the interface due to sortability and first/last access + * @Type(T) + */ +public class LINKED_HASH_SET KEY_GENERIC_TYPE extends HASH_SET KEY_GENERIC_TYPE implements ORDERED_SET KEY_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_HASH_SET() { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Constructor that defines the minimum capacity + * @param minCapacity the minimum capacity the HashSet is allowed to be. + * @throws IllegalStateException if the minimum capacity is negative + */ + public LINKED_HASH_SET(int minCapacity) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Constructor that defines the minimum capacity and load factor + * @param minCapacity the minimum capacity the HashSet 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_HASH_SET(int minCapacity, float loadFactor) { + super(minCapacity, loadFactor); + links = new long[nullIndex + 1]; + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + */ + public LINKED_HASH_SET(KEY_TYPE[] array) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @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_HASH_SET(KEY_TYPE[] array, float loadFactor) { + this(array, 0, array.length, loadFactor); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public LINKED_HASH_SET(KEY_TYPE[] array, int offset, int length) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @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 + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public LINKED_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor) { + this(length); + SanityChecks.checkArrayCapacity(array.length, offset, length); + for(int i = 0;i collection) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @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 + */ + @Primitive + public LINKED_HASH_SET(Collection collection, float loadFactor) { + this(collection.size(), loadFactor); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + */ + public LINKED_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @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_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor) { + this(collection.size()); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + */ + public LINKED_HASH_SET(Iterator iterator) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @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_HASH_SET(Iterator iterator, float loadFactor) { +#if !TYPE_OBJECT + this(ITERATORS.wrap(iterator), loadFactor); +#else + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); + while(iterator.hasNext()) add(iterator.next()); +#endif + } + +#if !TYPE_OBJECT + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + */ + public LINKED_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @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_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor) { + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); + while(iterator.hasNext()) add(iterator.NEXT()); + } + +#endif + @Override + public boolean addAndMoveToFirst(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) { + if(containsNull) { + moveToFirstIndex(nullIndex); + return false; + } + containsNull = true; + onNodeAdded(nullIndex); + moveToFirstIndex(nullIndex); + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos], o)) { + moveToFirstIndex(pos); + return false; + } + pos = ++pos & mask; + } + keys[pos] = o; + onNodeAdded(pos); + moveToFirstIndex(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + public boolean addAndMoveToLast(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) { + if(containsNull) { + moveToLastIndex(nullIndex); + return false; + } + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos], o)) { + moveToLastIndex(pos); + return false; + } + pos = ++pos & mask; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + + @Override + public boolean moveToFirst(KEY_TYPE o) { + if(isEmpty() || KEY_EQUALS(FIRST_KEY(), o)) return false; + if(KEY_EQUALS_NULL(o)) { + if(containsNull) { + moveToFirstIndex(nullIndex); + return true; + } + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos], o)) { + moveToFirstIndex(pos); + return true; + } + pos = ++pos & mask; + } + } + return false; + } + + @Override + public boolean moveToLast(KEY_TYPE o) { + if(isEmpty() || KEY_EQUALS(LAST_KEY(), o)) return false; + if(KEY_EQUALS_NULL(o)) { + if(containsNull) { + moveToLastIndex(nullIndex); + return true; + } + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos], o)) { + moveToLastIndex(pos); + return true; + } + pos = ++pos & mask; + } + } + return false; + } + + protected void moveToFirstIndex(int startPos) { + if(size == 1 || firstIndex == startPos) return; + if(lastIndex == startPos) { + lastIndex = (int)(links[startPos] >>> 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) { + if(size == 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 + public KEY_TYPE FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[firstIndex]; + } + + @Override + public KEY_TYPE POLL_FIRST_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = firstIndex; + onNodeRemoved(pos); + KEY_TYPE result = keys[pos]; + size--; + if(KEY_EQUALS_NULL(result)) { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return result; + } + + @Override + public KEY_TYPE LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + return keys[lastIndex]; + } + + @Override + public KEY_TYPE POLL_LAST_KEY() { + if(size == 0) throw new NoSuchElementException(); + int pos = lastIndex; + onNodeRemoved(pos); + KEY_TYPE result = keys[pos]; + size--; + if(KEY_EQUALS_NULL(result)) { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return result; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { + if(a == null || a.length < size()) a = new KEY_TYPE[size()]; + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + a[i] = keys[index]; + } + if (a.length > size) a[size] = EMPTY_KEY_VALUE; + return a; + } + +#endif + @Override + @Deprecated + public Object[] toArray() { + if(isEmpty()) return ObjectArrays.EMPTY_ARRAY; + Object[] obj = new Object[size()]; + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + obj[i] = KEY_TO_OBJ(keys[index]); + } + return obj; + } + + @Override + @Primitive + public E[] toArray(E[] a) { + if(a == null) a = (E[])new Object[size()]; + else if(a.length < size()) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size()); + for(int i = 0, index = firstIndex;index != -1;i++,index = (int)links[index]) { + a[i] = (E)KEY_TO_OBJ(keys[index]); + } + if (a.length > size) a[size] = null; + return a; + } + + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1){ + action.accept(keys[index]); + index = (int)links[index]; + } + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int count = 0; + int index = firstIndex; + while(index != -1) { + action.accept(count++, keys[index]); + index = (int)links[index]; + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + int index = firstIndex; + while(index != -1) { + action.accept(input, keys[index]); + index = (int)links[index]; + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return true; + index = (int)links[index]; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return false; + index = (int)links[index]; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(!filter.test(keys[index])) return false; + index = (int)links[index]; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + int index = firstIndex; + while(index != -1) { + state = operator.APPLY_VALUE(state, keys[index]); + 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_VALUE(state, keys[index]); + index = (int)links[index]; + } + return state; + } + +#endif + @Override + public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_VALUE; + boolean empty = true; + int index = firstIndex; + while(index != -1) { + if(empty) { + state = keys[index]; + empty = false; + } + else state = operator.APPLY_VALUE(state, keys[index]); + index = (int)links[index]; + } + return state; + } + + @Override + public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) return keys[index]; + index = (int)links[index]; + } + return EMPTY_VALUE; + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + int result = 0; + int index = firstIndex; + while(index != -1) { + if(filter.test(keys[index])) result++; + index = (int)links[index]; + } + return result; + } + + @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; + KEY_TYPE[] newKeys = NEW_KEY_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(KEY_TO_HASH(keys[i])) & newMask; + while(KEY_EQUALS_NOT_NULL(newKeys[pos])) pos = ++pos & newMask; + } + newKeys[pos] = keys[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; + nullIndex = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = newKeys; + } + + @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 >= nullIndex) { + clear(); + return; + } + nullIndex = request; + mask = request-1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = NEW_KEY_ARRAY(request + 1); + links = new long[request + 1]; + firstIndex = lastIndex = -1; + this.size = 0; + containsNull = false; + } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + + @Override + public BI_ITERATOR KEY_GENERIC_TYPE iterator(KEY_TYPE fromElement) { + return new SetIterator(fromElement); + } + + @Override + public LINKED_HASH_SET KEY_GENERIC_TYPE copy() { + LINKED_HASH_SET KEY_GENERIC_TYPE set = new LINKED_HASH_SETBRACES(0, loadFactor); + set.minCapacity = minCapacity; + set.mask = mask; + set.maxFill = maxFill; + set.nullIndex = nullIndex; + set.containsNull = containsNull; + set.size = size; + set.keys = Arrays.copyOf(keys, keys.length); + set.links = Arrays.copyOf(links, links.length); + set.firstIndex = firstIndex; + set.lastIndex = lastIndex; + return set; + } + + private class SetIterator implements LIST_ITERATOR KEY_GENERIC_TYPE { + int previous = -1; + int next = -1; + int current = -1; + int index = 0; + + SetIterator() { + next = firstIndex; + } + + SetIterator(KEY_TYPE from) { + if(KEY_EQUALS_NULL(from)) { + if(containsNull) { + next = (int) links[nullIndex]; + previous = nullIndex; + } + else throw new NoSuchElementException("The null element is not in the set"); + } + else if(KEY_EQUALS(keys[lastIndex], from)) { + previous = lastIndex; + index = size; + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(from)) & mask; + while(KEY_EQUALS_NOT_NULL(keys[pos])) { + if(KEY_EQUALS(keys[pos], from)) { + next = (int)links[pos]; + previous = pos; + break; + } + pos = ++pos & mask; + } + if(previous == -1 && next == -1) + throw new NoSuchElementException("The element was not found"); + } + } + + @Override + public int skip(int amount) { + int result = 0; + while(next != -1 && result != amount) { + current = previous = next; + next = (int)(links[current]); + result++; + } + if(index >= 0) index+=result; + return result; + } + + @Override + public int back(int amount) { + int result = 0; + while(previous != -1 && result != amount) { + current = next = previous; + previous = (int)(links[current] >> 32); + result++; + } + if(index >= 0) index-=result; + return result; + } + + @Override + public boolean hasNext() { + return next != -1; + } + + @Override + public boolean hasPrevious() { + return previous != -1; + } + + @Override + public int nextIndex() { + ensureIndexKnown(); + return index; + } + + @Override + public int previousIndex() { + ensureIndexKnown(); + return index - 1; + } + + @Override + 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 == nullIndex) { + current = -1; + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else { + int slot, last, startPos = current; + current = -1; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(KEY_EQUALS_NULL((current = keys[startPos]))) { + keys[last] = EMPTY_KEY_VALUE; + return; + } + slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current; + if(next == startPos) next = last; + if(previous == startPos) previous = last; + onNodeMoved(startPos, last); + } + } + } + + @Override + public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); + current = next = previous; + previous = (int)(links[current] >> 32); + if(index >= 0) index--; + return keys[current]; + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + current = previous = next; + next = (int)(links[current]); + if(index >= 0) index++; + return keys[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++); + } + } + } + +#if TYPE_OBJECT + @Override + public void set(Object e) { throw new UnsupportedOperationException(); } + + @Override + public void add(Object e) { throw new UnsupportedOperationException(); } +#else + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } +#endif + } +} diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template index 6ac37b0..29975d2 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template @@ -1,736 +1,772 @@ -package speiger.src.collections.PACKAGE.sets; - -import java.util.Arrays; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; -#if TYPE_OBJECT -import java.util.function.Consumer; -import java.util.function.BiFunction; -#endif -#if JDK_FUNCTION -import java.util.function.PREDICATE; -#endif - -import speiger.src.collections.PACKAGE.collections.COLLECTION; -import speiger.src.collections.PACKAGE.collections.ITERATOR; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.utils.ITERATORS; -import speiger.src.collections.PACKAGE.functions.CONSUMER; -#endif -import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; -import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; -#if !JDK_FUNCTION -import speiger.src.collections.PACKAGE.functions.function.PREDICATE; -#endif -import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; -import speiger.src.collections.PACKAGE.utils.STRATEGY; - -import speiger.src.collections.utils.HashUtil; -import speiger.src.collections.utils.ITrimmable; -import speiger.src.collections.utils.SanityChecks; - -/** - * A Type Specific HashSet that allows for custom HashControl. - * For cases where Objects/primitive do not allow hashcoding this can be really useful and provide a lot of control. - * @Type(T) - */ -public class CUSTOM_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements ITrimmable -{ - /** The Backing keys array */ - protected transient KEY_TYPE[] keys; - /** If a null value is present */ - protected transient boolean containsNull; - /** Minimum array size the HashSet will be */ - protected transient int minCapacity; - /** Index of the Null Value */ - protected transient int nullIndex; - /** Maximum amount of Values that can be stored before the array gets expanded usually 75% */ - protected transient int maxFill; - /** Max Index that is allowed to be searched through nullIndex - 1 */ - protected transient int mask; - - /** Amount of Elements stored in the HashSet */ - protected int size; - /** How full the Array is allowed to get before resize */ - protected final float loadFactor; - /** Strategy that allows to control the Hash Generation and equals comparason */ - protected final STRATEGY KEY_SUPER_GENERIC_TYPE strategy; - - /** - * Default Contstructor - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public CUSTOM_HASH_SET(STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Constructor that defines the minimum capacity - * @param minCapacity the minimum capacity the HashSet is allowed to be. - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the minimum capacity is negative - */ - public CUSTOM_HASH_SET(int minCapacity, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Constructor that defines the minimum capacity and load factor - * @param minCapacity the minimum capacity the HashSet is allowed to be. - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @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 CUSTOM_HASH_SET(int minCapacity, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - if(minCapacity < 0) throw new IllegalStateException("Minimum Capacity is negative. This is not allowed"); - if(loadFactor <= 0 || loadFactor >= 1F) throw new IllegalStateException("Load Factor is not between 0 and 1"); - this.loadFactor = loadFactor; - this.minCapacity = nullIndex = HashUtil.arraySize(minCapacity, loadFactor); - mask = nullIndex - 1; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = NEW_KEY_ARRAY(nullIndex + 1); - this.strategy = strategy; - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public CUSTOM_HASH_SET(KEY_TYPE[] array, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public CUSTOM_HASH_SET(KEY_TYPE[] array, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(array, 0, array.length, loadFactor, strategy); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(length < 0 ? 0 : length, strategy); - SanityChecks.checkArrayCapacity(array.length, offset, length); - for(int i = 0;i collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - @Primitive - public CUSTOM_HASH_SET(Collection collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection.size(), loadFactor, strategy); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(collection.size(), strategy); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public CUSTOM_HASH_SET(Iterator iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public CUSTOM_HASH_SET(Iterator iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { -#if !TYPE_OBJECT - this(ITERATORS.wrap(iterator), loadFactor, strategy); -#else - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); - while(iterator.hasNext()) add(iterator.next()); -#endif - } - -#if !TYPE_OBJECT - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - */ - public CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @param loadFactor the percentage of how full the backing array can be before they resize - * @param strategy the strategy that allows hash control. - * @throws NullPointerException if Strategy is null - * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 - */ - public CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); - while(iterator.hasNext()) add(iterator.NEXT()); - } - -#endif - /** - * Helper getter function to get the current strategy - * @return the current strategy - */ - public STRATEGY KEY_SUPER_GENERIC_TYPE getStrategy() { - return strategy; - } - - @Override - public boolean add(KEY_TYPE o) { - if(strategy.equals(o, EMPTY_KEY_VALUE)) { - if(containsNull) return false; - containsNull = true; - onNodeAdded(nullIndex); - } - else { - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - KEY_TYPE current = keys[pos]; - if(!strategy.equals(current, EMPTY_KEY_VALUE)) { - if(strategy.equals(current, o)) return false; - while(!strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) - if(strategy.equals(current, o)) return false; - } - keys[pos] = o; - onNodeAdded(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return true; - } - -#if TYPE_OBJECT - @Override - public KEY_TYPE addOrGet(KEY_TYPE o) { - if(strategy.equals(o, EMPTY_KEY_VALUE)) { - if(containsNull) return EMPTY_KEY_VALUE; - containsNull = true; - onNodeAdded(nullIndex); - } - else { - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - KEY_TYPE current = keys[pos]; - if(!strategy.equals(current, EMPTY_KEY_VALUE)) { - if(strategy.equals(current, o)) return current; - while(!strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) - if(strategy.equals(current, o)) return current; - } - keys[pos] = o; - onNodeAdded(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return o; - } - -#endif - @Override - @Primitive - public boolean addAll(Collection c) { - if(loadFactor <= 0.5F) ensureCapacity(c.size()); - else ensureCapacity(c.size() + size()); - return super.addAll(c); - } - - @Override - public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { - if(loadFactor <= 0.5F) ensureCapacity(c.size()); - else ensureCapacity(c.size() + size()); - return super.addAll(c); - } - -#if TYPE_OBJECT - @Override - public boolean contains(Object o) { - if(strategy.equals((KEY_TYPE)o, EMPTY_KEY_VALUE)) return containsNull; - int pos = HashUtil.mix(strategy.hashCode((KEY_TYPE)o)) & mask; - KEY_TYPE current = keys[pos]; - if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; - if(strategy.equals(current, (KEY_TYPE)o)) return true; - while(true) { - if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; - else if(strategy.equals(current, (KEY_TYPE)o)) return true; - } - } - - @Override - public boolean remove(Object o) { - if(strategy.equals((KEY_TYPE)o, EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false); - int pos = HashUtil.mix(strategy.hashCode((KEY_TYPE)o)) & mask; - KEY_TYPE current = keys[pos]; - if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; - if(strategy.equals(current, (KEY_TYPE)o)) return removeIndex(pos); - while(true) { - if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; - else if(strategy.equals(current, (KEY_TYPE)o)) return removeIndex(pos); - } - } - -#else - @Override - public boolean contains(KEY_TYPE o) { - if(strategy.equals(o, EMPTY_KEY_VALUE)) return containsNull; - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - KEY_TYPE current = keys[pos]; - if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; - if(strategy.equals(current, o)) return true; - while(true) { - if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; - else if(strategy.equals(current, o)) return true; - } - } - - @Override - public boolean remove(KEY_TYPE o) { - if(strategy.equals(o, EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false); - int pos = HashUtil.mix(strategy.hashCode(o)) & mask; - KEY_TYPE current = keys[pos]; - if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; - if(strategy.equals(current, o)) return removeIndex(pos); - while(true) { - if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; - else if(strategy.equals(current, o)) return removeIndex(pos); - } - } - -#endif - @Override - public boolean trim(int size) { - int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); - if(request >= nullIndex || this.size >= Math.min((int)Math.ceil(request * loadFactor), request - 1)) return false; - try { - rehash(request); - } - catch(OutOfMemoryError e) { return false; } - return true; - } - - @Override - public void clearAndTrim(int size) { - int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); - if(request >= nullIndex) { - clear(); - return; - } - nullIndex = request; - mask = request-1; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = NEW_KEY_ARRAY(request + 1); - this.size = 0; - containsNull = false; - } - - private void ensureCapacity(int newCapacity) { - int size = HashUtil.arraySize(newCapacity, loadFactor); - if(size > nullIndex) rehash(size); - } - - protected boolean removeIndex(int pos) { - if(pos == nullIndex) return containsNull ? removeNullIndex() : false; - keys[pos] = EMPTY_KEY_VALUE; - size--; - onNodeRemoved(pos); - shiftKeys(pos); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return true; - } - - protected boolean removeNullIndex() { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - size--; - onNodeRemoved(nullIndex); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return true; - } - - protected void onNodeAdded(int pos) { - - } - - protected void onNodeRemoved(int pos) { - - } - - protected void onNodeMoved(int from, int to) { - - } - - protected void shiftKeys(int startPos) { - int slot, last; - KEY_TYPE current; - while(true) { - startPos = ((last = startPos) + 1) & mask; - while(true){ - if(strategy.equals((current = keys[startPos]), EMPTY_KEY_VALUE)) { - keys[last] = EMPTY_KEY_VALUE; - return; - } - slot = HashUtil.mix(strategy.hashCode(current)) & mask; - if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; - startPos = ++startPos & mask; - } - keys[last] = current; - onNodeMoved(startPos, last); - } - } - - protected void rehash(int newSize) { - int newMask = newSize - 1; - KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); - for(int i = nullIndex, pos = 0, j = (size - (containsNull ? 1 : 0));j-- != 0;) { - while(true) { - if(--i < 0) throw new ConcurrentModificationException("Set was modified during rehash"); - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) break; - } - if(!strategy.equals(newKeys[pos = HashUtil.mix(KEY_TO_HASH(keys[i])) & newMask], EMPTY_KEY_VALUE)) - while(!strategy.equals(newKeys[pos = (++pos & newMask)], EMPTY_KEY_VALUE)); - newKeys[pos] = keys[i]; - } - nullIndex = newSize; - mask = newMask; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = newKeys; - } - - @Override - public ITERATOR KEY_GENERIC_TYPE iterator() { - return new SetIterator(); - } - - @Override - public CUSTOM_HASH_SET KEY_GENERIC_TYPE copy() { - CUSTOM_HASH_SET KEY_GENERIC_TYPE set = new CUSTOM_HASH_SETBRACES(0, loadFactor, strategy); - set.minCapacity = minCapacity; - set.mask = mask; - set.maxFill = maxFill; - set.nullIndex = nullIndex; - set.containsNull = containsNull; - set.size = size; - set.keys = Arrays.copyOf(keys, keys.length); - return set; - } - - @Override - public void clear() { - if(size == 0) return; - size = 0; - containsNull = false; - Arrays.fill(keys, EMPTY_KEY_VALUE); - } - - @Override - public int size() { - return size; - } - - @Override - public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { - Objects.requireNonNull(action); - if(size() <= 0) return; - if(containsNull) action.accept(keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(keys[i]); - } - } - - @Override - public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { - Objects.requireNonNull(action); - if(size() <= 0) return; - if(containsNull) action.accept(0, keys[nullIndex]); - for(int i = nullIndex-1, index = containsNull ? 1 : 0;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(index++, keys[i]); - } - } - - @Override - public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { - Objects.requireNonNull(action); - if(size() <= 0) return; - if(containsNull) action.accept(input, keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(input, keys[i]); - } - } - - @Override - public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return false; - if(containsNull && filter.test(keys[nullIndex])) return true; - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) return true; - } - return false; - } - - @Override - public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return true; - if(containsNull && filter.test(keys[nullIndex])) return false; - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) return false; - } - return true; - } - - @Override - public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return true; - if(containsNull && !filter.test(keys[nullIndex])) return false; - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && !filter.test(keys[i])) return false; - } - return true; - } - -#if !TYPE_OBJECT - @Override - public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = identity; - if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) continue; - state = operator.APPLY_VALUE(state, keys[i]); - } - return state; - } - -#else - @Override - public KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { - Objects.requireNonNull(operator); - KEY_SPECIAL_TYPE state = identity; - if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) continue; - state = operator.APPLY_VALUE(state, keys[i]); - } - return state; - } - -#endif - @Override - public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = EMPTY_VALUE; - boolean empty = true; - if(containsNull) { - state = keys[nullIndex]; - empty = false; - } - for(int i = nullIndex-1;i>=0;i--) { - if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) continue; - if(empty) { - empty = false; - state = keys[i]; - continue; - } - state = operator.APPLY_VALUE(state, keys[i]); - } - return state; - } - - @Override - public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return EMPTY_VALUE; - if(containsNull && filter.test(keys[nullIndex])) return keys[nullIndex]; - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) return keys[i]; - } - return EMPTY_VALUE; - } - - @Override - public int count(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return 0; - int result = 0; - if(containsNull && filter.test(keys[nullIndex])) result++; - for(int i = nullIndex-1;i>=0;i--) { - if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) result++; - } - return result; - } - - private class SetIterator implements ITERATOR KEY_GENERIC_TYPE { - int pos = nullIndex; - int returnedPos = -1; - int lastReturned = -1; - int nextIndex = Integer.MIN_VALUE; - boolean returnNull = containsNull; - KEY_TYPE[] wrapped = null; - int wrappedIndex = 0; - - @Override - public boolean hasNext() { - if(nextIndex == Integer.MIN_VALUE) { - if(returnNull) { - returnNull = false; - nextIndex = nullIndex; - } - else { - 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; - } - - @Override - public KEY_TYPE NEXT() { - if(!hasNext()) throw new NoSuchElementException(); - returnedPos = pos; - if(nextIndex < 0){ - lastReturned = Integer.MAX_VALUE; - KEY_TYPE value = wrapped[nextIndex]; - nextIndex = Integer.MIN_VALUE; - return value; - } - KEY_TYPE value = keys[(lastReturned = nextIndex)]; - nextIndex = Integer.MIN_VALUE; - return value; - } - - @Override - public void remove() { - if(lastReturned == -1) throw new IllegalStateException(); - if(lastReturned == nullIndex) { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else if(returnedPos >= 0) shiftKeys(returnedPos); - else { - CUSTOM_HASH_SET.this.remove(wrapped[-returnedPos - 1]); - lastReturned = -1; - return; - } - size--; - lastReturned = -1; - } - - private void shiftKeys(int startPos) { - int slot, last; - KEY_TYPE current; - while(true) { - startPos = ((last = startPos) + 1) & mask; - while(true){ - if(strategy.equals((current = keys[startPos]), EMPTY_KEY_VALUE)) { - keys[last] = EMPTY_KEY_VALUE; - return; - } - slot = HashUtil.mix(strategy.hashCode(current)) & 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; - } - } - - private void addWrapper(KEY_TYPE value) { - if(wrapped == null) wrapped = NEW_KEY_ARRAY(2); - else if(wrappedIndex >= wrapped.length) { - KEY_TYPE[] newArray = NEW_KEY_ARRAY(wrapped.length * 2); - System.arraycopy(wrapped, 0, newArray, 0, wrapped.length); - wrapped = newArray; - } - wrapped[wrappedIndex++] = value; - } - } +package speiger.src.collections.PACKAGE.sets; + +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +#if TYPE_OBJECT +import java.util.function.Consumer; +import java.util.function.BiFunction; +#endif +#if JDK_FUNCTION +import java.util.function.PREDICATE; +#endif + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.utils.ITERATORS; +import speiger.src.collections.PACKAGE.functions.CONSUMER; +#endif +import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; +import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; +#if !JDK_FUNCTION +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; +import speiger.src.collections.PACKAGE.utils.STRATEGY; +import speiger.src.collections.objects.utils.ObjectArrays; + +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.ITrimmable; +import speiger.src.collections.utils.SanityChecks; + +/** + * A Type Specific HashSet that allows for custom HashControl. + * For cases where Objects/primitive do not allow hashcoding this can be really useful and provide a lot of control. + * @Type(T) + */ +public class CUSTOM_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements ITrimmable +{ + /** The Backing keys array */ + protected transient KEY_TYPE[] keys; + /** If a null value is present */ + protected transient boolean containsNull; + /** Minimum array size the HashSet will be */ + protected transient int minCapacity; + /** Index of the Null Value */ + protected transient int nullIndex; + /** Maximum amount of Values that can be stored before the array gets expanded usually 75% */ + protected transient int maxFill; + /** Max Index that is allowed to be searched through nullIndex - 1 */ + protected transient int mask; + + /** Amount of Elements stored in the HashSet */ + protected int size; + /** How full the Array is allowed to get before resize */ + protected final float loadFactor; + /** Strategy that allows to control the Hash Generation and equals comparason */ + protected final STRATEGY KEY_SUPER_GENERIC_TYPE strategy; + + /** + * Default Contstructor + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public CUSTOM_HASH_SET(STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Constructor that defines the minimum capacity + * @param minCapacity the minimum capacity the HashSet is allowed to be. + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the minimum capacity is negative + */ + public CUSTOM_HASH_SET(int minCapacity, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Constructor that defines the minimum capacity and load factor + * @param minCapacity the minimum capacity the HashSet is allowed to be. + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @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 CUSTOM_HASH_SET(int minCapacity, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + if(minCapacity < 0) throw new IllegalStateException("Minimum Capacity is negative. This is not allowed"); + if(loadFactor <= 0 || loadFactor >= 1F) throw new IllegalStateException("Load Factor is not between 0 and 1"); + this.loadFactor = loadFactor; + this.minCapacity = nullIndex = HashUtil.arraySize(minCapacity, loadFactor); + mask = nullIndex - 1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = NEW_KEY_ARRAY(nullIndex + 1); + this.strategy = strategy; + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public CUSTOM_HASH_SET(KEY_TYPE[] array, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public CUSTOM_HASH_SET(KEY_TYPE[] array, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, 0, array.length, loadFactor, strategy); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public CUSTOM_HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(length < 0 ? 0 : length, strategy); + SanityChecks.checkArrayCapacity(array.length, offset, length); + for(int i = 0;i collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + @Primitive + public CUSTOM_HASH_SET(Collection collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), loadFactor, strategy); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public CUSTOM_HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(collection.size(), strategy); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public CUSTOM_HASH_SET(Iterator iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public CUSTOM_HASH_SET(Iterator iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { +#if !TYPE_OBJECT + this(ITERATORS.wrap(iterator), loadFactor, strategy); +#else + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.next()); +#endif + } + +#if !TYPE_OBJECT + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + */ + public CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR, strategy); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @param loadFactor the percentage of how full the backing array can be before they resize + * @param strategy the strategy that allows hash control. + * @throws NullPointerException if Strategy is null + * @throws IllegalStateException if the loadfactor is either below/equal to 0 or above/equal to 1 + */ + public CUSTOM_HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor, STRATEGY KEY_SUPER_GENERIC_TYPE strategy) { + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor, strategy); + while(iterator.hasNext()) add(iterator.NEXT()); + } + +#endif + /** + * Helper getter function to get the current strategy + * @return the current strategy + */ + public STRATEGY KEY_SUPER_GENERIC_TYPE getStrategy() { + return strategy; + } + + @Override + public boolean add(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_KEY_VALUE)) { + if(containsNull) return false; + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_KEY_VALUE)) { + if(strategy.equals(current, o)) return false; + while(!strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) + if(strategy.equals(current, o)) return false; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + +#if TYPE_OBJECT + @Override + public KEY_TYPE addOrGet(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_KEY_VALUE)) { + if(containsNull) return EMPTY_KEY_VALUE; + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(!strategy.equals(current, EMPTY_KEY_VALUE)) { + if(strategy.equals(current, o)) return current; + while(!strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) + if(strategy.equals(current, o)) return current; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return o; + } + +#endif + @Override + @Primitive + public boolean addAll(Collection c) { + if(loadFactor <= 0.5F) ensureCapacity(c.size()); + else ensureCapacity(c.size() + size()); + return super.addAll(c); + } + + @Override + public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { + if(loadFactor <= 0.5F) ensureCapacity(c.size()); + else ensureCapacity(c.size() + size()); + return super.addAll(c); + } + +#if TYPE_OBJECT + @Override + public boolean contains(Object o) { + if(strategy.equals((KEY_TYPE)o, EMPTY_KEY_VALUE)) return containsNull; + int pos = HashUtil.mix(strategy.hashCode((KEY_TYPE)o)) & mask; + KEY_TYPE current = keys[pos]; + if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; + if(strategy.equals(current, (KEY_TYPE)o)) return true; + while(true) { + if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; + else if(strategy.equals(current, (KEY_TYPE)o)) return true; + } + } + + @Override + public boolean remove(Object o) { + if(strategy.equals((KEY_TYPE)o, EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false); + int pos = HashUtil.mix(strategy.hashCode((KEY_TYPE)o)) & mask; + KEY_TYPE current = keys[pos]; + if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; + if(strategy.equals(current, (KEY_TYPE)o)) return removeIndex(pos); + while(true) { + if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; + else if(strategy.equals(current, (KEY_TYPE)o)) return removeIndex(pos); + } + } + +#else + @Override + public boolean contains(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_KEY_VALUE)) return containsNull; + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; + if(strategy.equals(current, o)) return true; + while(true) { + if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; + else if(strategy.equals(current, o)) return true; + } + } + + @Override + public boolean remove(KEY_TYPE o) { + if(strategy.equals(o, EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false); + int pos = HashUtil.mix(strategy.hashCode(o)) & mask; + KEY_TYPE current = keys[pos]; + if(strategy.equals(current, EMPTY_KEY_VALUE)) return false; + if(strategy.equals(current, o)) return removeIndex(pos); + while(true) { + if(strategy.equals((current = keys[pos = (++pos & mask)]), EMPTY_KEY_VALUE)) return false; + else if(strategy.equals(current, o)) return removeIndex(pos); + } + } + +#endif + @Override + public boolean trim(int size) { + int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); + if(request >= nullIndex || this.size >= Math.min((int)Math.ceil(request * loadFactor), request - 1)) return false; + try { + rehash(request); + } + catch(OutOfMemoryError e) { return false; } + return true; + } + + @Override + public void clearAndTrim(int size) { + int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); + if(request >= nullIndex) { + clear(); + return; + } + nullIndex = request; + mask = request-1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = NEW_KEY_ARRAY(request + 1); + this.size = 0; + containsNull = false; + } + + private void ensureCapacity(int newCapacity) { + int size = HashUtil.arraySize(newCapacity, loadFactor); + if(size > nullIndex) rehash(size); + } + + protected boolean removeIndex(int pos) { + if(pos == nullIndex) return containsNull ? removeNullIndex() : false; + keys[pos] = EMPTY_KEY_VALUE; + size--; + onNodeRemoved(pos); + shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return true; + } + + protected boolean removeNullIndex() { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + size--; + onNodeRemoved(nullIndex); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return true; + } + + protected void onNodeAdded(int pos) { + + } + + protected void onNodeRemoved(int pos) { + + } + + protected void onNodeMoved(int from, int to) { + + } + + protected void shiftKeys(int startPos) { + int slot, last; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(strategy.equals((current = keys[startPos]), EMPTY_KEY_VALUE)) { + keys[last] = EMPTY_KEY_VALUE; + return; + } + slot = HashUtil.mix(strategy.hashCode(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current; + onNodeMoved(startPos, last); + } + } + + protected void rehash(int newSize) { + int newMask = newSize - 1; + KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); + for(int i = nullIndex, pos = 0, j = (size - (containsNull ? 1 : 0));j-- != 0;) { + while(true) { + if(--i < 0) throw new ConcurrentModificationException("Set was modified during rehash"); + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) break; + } + if(!strategy.equals(newKeys[pos = HashUtil.mix(KEY_TO_HASH(keys[i])) & newMask], EMPTY_KEY_VALUE)) + while(!strategy.equals(newKeys[pos = (++pos & newMask)], EMPTY_KEY_VALUE)); + newKeys[pos] = keys[i]; + } + nullIndex = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = newKeys; + } + + @Override + public ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { + if(a == null || a.length < size()) a = new KEY_TYPE[size()]; + for(int i = nullIndex-1, index = 0;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) a[index++] = keys[i]; + } + if (a.length > size) a[size] = EMPTY_KEY_VALUE; + return a; + } + +#endif + @Override + @Deprecated + public Object[] toArray() { + if(isEmpty()) return ObjectArrays.EMPTY_ARRAY; + Object[] obj = new Object[size()]; + for(int i = nullIndex-1, index = 0;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) obj[index++] = KEY_TO_OBJ(keys[i]); + } + return obj; + } + + @Override + @Primitive + public E[] toArray(E[] a) { + if(a == null) a = (E[])new Object[size()]; + else if(a.length < size()) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size()); + for(int i = nullIndex-1, index = 0;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) a[index++] = (E)KEY_TO_OBJ(keys[i]); + } + if (a.length > size) a[size] = null; + return a; + } + + @Override + public CUSTOM_HASH_SET KEY_GENERIC_TYPE copy() { + CUSTOM_HASH_SET KEY_GENERIC_TYPE set = new CUSTOM_HASH_SETBRACES(0, loadFactor, strategy); + set.minCapacity = minCapacity; + set.mask = mask; + set.maxFill = maxFill; + set.nullIndex = nullIndex; + set.containsNull = containsNull; + set.size = size; + set.keys = Arrays.copyOf(keys, keys.length); + return set; + } + + @Override + public void clear() { + if(size == 0) return; + size = 0; + containsNull = false; + Arrays.fill(keys, EMPTY_KEY_VALUE); + } + + @Override + public int size() { + return size; + } + + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + if(containsNull) action.accept(keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(keys[i]); + } + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + if(containsNull) action.accept(0, keys[nullIndex]); + for(int i = nullIndex-1, index = containsNull ? 1 : 0;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(index++, keys[i]); + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + if(containsNull) action.accept(input, keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE)) action.accept(input, keys[i]); + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + if(containsNull && filter.test(keys[nullIndex])) return true; + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + if(containsNull && filter.test(keys[nullIndex])) return false; + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + if(containsNull && !filter.test(keys[nullIndex])) return false; + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && !filter.test(keys[i])) return false; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) continue; + state = operator.APPLY_VALUE(state, keys[i]); + } + return state; + } + +#else + @Override + public KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { + Objects.requireNonNull(operator); + KEY_SPECIAL_TYPE state = identity; + if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) continue; + state = operator.APPLY_VALUE(state, keys[i]); + } + return state; + } + +#endif + @Override + public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_VALUE; + boolean empty = true; + if(containsNull) { + state = keys[nullIndex]; + empty = false; + } + for(int i = nullIndex-1;i>=0;i--) { + if(strategy.equals(keys[i], EMPTY_KEY_VALUE)) continue; + if(empty) { + empty = false; + state = keys[i]; + continue; + } + state = operator.APPLY_VALUE(state, keys[i]); + } + return state; + } + + @Override + public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return EMPTY_VALUE; + if(containsNull && filter.test(keys[nullIndex])) return keys[nullIndex]; + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) return keys[i]; + } + return EMPTY_VALUE; + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + if(containsNull && filter.test(keys[nullIndex])) result++; + for(int i = nullIndex-1;i>=0;i--) { + if(!strategy.equals(keys[i], EMPTY_KEY_VALUE) && filter.test(keys[i])) result++; + } + return result; + } + + private class SetIterator implements ITERATOR KEY_GENERIC_TYPE { + int pos = nullIndex; + int returnedPos = -1; + int lastReturned = -1; + int nextIndex = Integer.MIN_VALUE; + boolean returnNull = containsNull; + KEY_TYPE[] wrapped = null; + int wrappedIndex = 0; + + @Override + public boolean hasNext() { + if(nextIndex == Integer.MIN_VALUE) { + if(returnNull) { + returnNull = false; + nextIndex = nullIndex; + } + else { + 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; + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + returnedPos = pos; + if(nextIndex < 0){ + lastReturned = Integer.MAX_VALUE; + KEY_TYPE value = wrapped[nextIndex]; + nextIndex = Integer.MIN_VALUE; + return value; + } + KEY_TYPE value = keys[(lastReturned = nextIndex)]; + nextIndex = Integer.MIN_VALUE; + return value; + } + + @Override + public void remove() { + if(lastReturned == -1) throw new IllegalStateException(); + if(lastReturned == nullIndex) { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else if(returnedPos >= 0) shiftKeys(returnedPos); + else { + CUSTOM_HASH_SET.this.remove(wrapped[-returnedPos - 1]); + lastReturned = -1; + return; + } + size--; + lastReturned = -1; + } + + private void shiftKeys(int startPos) { + int slot, last; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(strategy.equals((current = keys[startPos]), EMPTY_KEY_VALUE)) { + keys[last] = EMPTY_KEY_VALUE; + return; + } + slot = HashUtil.mix(strategy.hashCode(current)) & 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; + } + } + + private void addWrapper(KEY_TYPE value) { + if(wrapped == null) wrapped = NEW_KEY_ARRAY(2); + else if(wrappedIndex >= wrapped.length) { + KEY_TYPE[] newArray = NEW_KEY_ARRAY(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/sets/OpenHashSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/OpenHashSet.template index fe19c15..a6a3df3 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/OpenHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/OpenHashSet.template @@ -1,703 +1,740 @@ -package speiger.src.collections.PACKAGE.sets; - -import java.util.Arrays; -import java.util.Collection; -import java.util.ConcurrentModificationException; -import java.util.Iterator; -import java.util.NoSuchElementException; -import java.util.Objects; -#if TYPE_OBJECT -import java.util.function.Consumer; -import java.util.function.BiFunction; -#endif -#if JDK_FUNCTION -import java.util.function.PREDICATE; -#endif - -import speiger.src.collections.PACKAGE.collections.COLLECTION; -import speiger.src.collections.PACKAGE.collections.ITERATOR; -#if !TYPE_OBJECT -import speiger.src.collections.PACKAGE.utils.ITERATORS; -import speiger.src.collections.PACKAGE.functions.CONSUMER; -#endif -import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; -import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; -#if !JDK_FUNCTION -import speiger.src.collections.PACKAGE.functions.function.PREDICATE; -#endif -import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; -import speiger.src.collections.utils.HashUtil; -import speiger.src.collections.utils.ITrimmable; -import speiger.src.collections.utils.SanityChecks; - -/** - * A Type Specific Custom implementation of the HashSet - * Instead of using Wrapper Object Arrays for storing keys and values there is dedicated arrays for storing keys. - * Extra to that there is a couple quality of life functions provided - * @Type(T) - */ -public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements ITrimmable -{ - /** The Backing keys array */ - protected transient KEY_TYPE[] keys; - /** If a null value is present */ - protected transient boolean containsNull; - /** Minimum array size the HashSet will be */ - protected transient int minCapacity; - /** Index of the Null Value */ - protected transient int nullIndex; - /** Maximum amount of Values that can be stored before the array gets expanded usually 75% */ - protected transient int maxFill; - /** Max Index that is allowed to be searched through nullIndex - 1 */ - protected transient int mask; - - /** Amount of Elements stored in the HashSet */ - protected int size; - /** How full the Array is allowed to get before resize */ - protected final float loadFactor; - - /** - * Default Constructor - */ - public HASH_SET() { - this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Constructor that defines the minimum capacity - * @param minCapacity the minimum capacity the HashSet is allowed to be. - * @throws IllegalStateException if the minimum capacity is negative - */ - public HASH_SET(int minCapacity) { - this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Constructor that defines the minimum capacity and load factor - * @param minCapacity the minimum capacity the HashSet 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 HASH_SET(int minCapacity, float loadFactor) { - if(minCapacity < 0) throw new IllegalStateException("Minimum Capacity is negative. This is not allowed"); - if(loadFactor <= 0 || loadFactor >= 1F) throw new IllegalStateException("Load Factor is not between 0 and 1"); - this.loadFactor = loadFactor; - this.minCapacity = nullIndex = HashUtil.arraySize(minCapacity, loadFactor); - mask = nullIndex - 1; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = NEW_KEY_ARRAY(nullIndex + 1); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - */ - public HASH_SET(KEY_TYPE[] array) { - this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @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 HASH_SET(KEY_TYPE[] array, float loadFactor) { - this(array, 0, array.length, loadFactor); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public HASH_SET(KEY_TYPE[] array, int offset, int length) { - this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * Helper constructor that allow to create a set from unboxed values - * @param array the elements that should be put into the set - * @param offset the starting index within the array that should be used - * @param length the amount of elements used from the array - * @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 - * @throws IllegalStateException if offset and length causes to step outside of the arrays range - */ - public HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor) { - this(length < 0 ? 0 : length); - SanityChecks.checkArrayCapacity(array.length, offset, length); - for(int i = 0;i collection) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @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 - */ - @Primitive - public HASH_SET(Collection collection, float loadFactor) { - this(collection.size(), loadFactor); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - */ - public HASH_SET(COLLECTION KEY_GENERIC_TYPE collection) { - this(collection, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. - * @param collection the set the elements should be added to the Set - * @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 HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor) { - this(collection.size()); - addAll(collection); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - */ - public HASH_SET(Iterator iterator) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @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 HASH_SET(Iterator iterator, float loadFactor) { -#if !TYPE_OBJECT - this(ITERATORS.wrap(iterator), loadFactor); -#else - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); - while(iterator.hasNext()) add(iterator.next()); -#endif - } - -#if !TYPE_OBJECT - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - */ - public HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator) { - this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); - } - - /** - * A Helper constructor that allows to create a set from a iterator of an unknown size - * @param iterator the elements that should be added to the set - * @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 HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor) { - this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); - while(iterator.hasNext()) add(iterator.NEXT()); - } - -#endif - @Override - public boolean add(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) { - if(containsNull) return false; - containsNull = true; - onNodeAdded(nullIndex); - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NOT_NULL(current)) { - if(KEY_EQUALS(current, o)) return false; - while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) - if(KEY_EQUALS(current, o)) return false; - } - keys[pos] = o; - onNodeAdded(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return true; - } - -#if TYPE_OBJECT - @Override - public KEY_TYPE addOrGet(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) { - if(containsNull) return null; - containsNull = true; - onNodeAdded(nullIndex); - } - else { - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NOT_NULL(current)) { - if(KEY_EQUALS(current, o)) return current; - while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) - if(KEY_EQUALS(current, o)) return current; - } - keys[pos] = o; - onNodeAdded(pos); - } - if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); - return o; - } - -#endif - @Override - @Primitive - public boolean addAll(Collection c) { - if(loadFactor <= 0.5F) ensureCapacity(c.size()); - else ensureCapacity(c.size() + size()); - return super.addAll(c); - } - - @Override - public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { - if(loadFactor <= 0.5F) ensureCapacity(c.size()); - else ensureCapacity(c.size() + size()); - return super.addAll(c); - } - - @Override - public boolean contains(Object o) { -#if TYPE_OBJECT - if(o == null) return containsNull; -#else - if(o == null) return false; - if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return containsNull; -#endif - int pos = HashUtil.mix(o.hashCode()) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NULL(current)) return false; - if(EQUALS_KEY_TYPE(current, o)) return true; - while(true) { - if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; - else if(EQUALS_KEY_TYPE(current, o)) return true; - } - } - - @Override - public boolean remove(Object o) { -#if TYPE_OBJECT - if(o == null) return (containsNull ? removeNullIndex() : false); -#else - if(o == null) return false; - if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false); -#endif - int pos = HashUtil.mix(o.hashCode()) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NULL(current)) return false; - if(EQUALS_KEY_TYPE(current, o)) return removeIndex(pos); - while(true) { - if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; - else if(EQUALS_KEY_TYPE(current, o)) return removeIndex(pos); - } - } - -#if !TYPE_OBJECT - @Override - public boolean contains(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) return containsNull; - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NULL(current)) return false; - if(KEY_EQUALS(current, o)) return true; - while(true) { - if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; - else if(KEY_EQUALS(current, o)) return true; - } - } - - @Override - public boolean remove(KEY_TYPE o) { - if(KEY_EQUALS_NULL(o)) return containsNull ? removeNullIndex() : false; - int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; - KEY_TYPE current = keys[pos]; - if(KEY_EQUALS_NULL(current)) return false; - if(KEY_EQUALS(current, o)) return removeIndex(pos); - while(true) { - if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; - else if(KEY_EQUALS(current, o)) return removeIndex(pos); - } - } - -#endif - @Override - public boolean trim(int size) { - int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); - if(request >= nullIndex || this.size >= Math.min((int)Math.ceil(request * loadFactor), request - 1)) return false; - try { - rehash(request); - } - catch(OutOfMemoryError e) { return false; } - return true; - } - - @Override - public void clearAndTrim(int size) { - int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); - if(request >= nullIndex) { - clear(); - return; - } - nullIndex = request; - mask = request-1; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = NEW_KEY_ARRAY(request + 1); - this.size = 0; - containsNull = false; - } - - @Override - public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { - if(size() <= 0) return; - if(containsNull) action.accept(keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i]); - } - } - - @Override - public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { - Objects.requireNonNull(action); - if(size() <= 0) return; - if(containsNull) action.accept(0, keys[nullIndex]); - for(int i = nullIndex-1, index = containsNull ? 1 : 0;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(index++, keys[i]); - } - } - - @Override - public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { - Objects.requireNonNull(action); - if(size() <= 0) return; - if(containsNull) action.accept(input, keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(input, keys[i]); - } - } - - @Override - public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return false; - if(containsNull && filter.test(keys[nullIndex])) return true; - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) return true; - } - return false; - } - - @Override - public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return true; - if(containsNull && filter.test(keys[nullIndex])) return false; - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) return false; - } - return true; - } - - @Override - public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return true; - if(containsNull && !filter.test(keys[nullIndex])) return false; - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i]) && !filter.test(keys[i])) return false; - } - return true; - } - -#if !TYPE_OBJECT - @Override - public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = identity; - if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NULL(keys[i])) continue; - state = operator.APPLY_VALUE(state, keys[i]); - } - return state; - } - -#else - @Override - public KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { - Objects.requireNonNull(operator); - KEY_SPECIAL_TYPE state = identity; - if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NULL(keys[i])) continue; - state = operator.APPLY_VALUE(state, keys[i]); - } - return state; - } - -#endif - @Override - public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { - Objects.requireNonNull(operator); - KEY_TYPE state = EMPTY_VALUE; - boolean empty = true; - if(containsNull) { - state = keys[nullIndex]; - empty = false; - } - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NULL(keys[i])) continue; - if(empty) { - empty = false; - state = keys[i]; - continue; - } - state = operator.APPLY_VALUE(state, keys[i]); - } - return state; - } - - @Override - public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return EMPTY_VALUE; - if(containsNull && filter.test(keys[nullIndex])) return keys[nullIndex]; - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) return keys[i]; - } - return EMPTY_VALUE; - } - - @Override - public int count(PREDICATE KEY_GENERIC_TYPE filter) { - Objects.requireNonNull(filter); - if(size() <= 0) return 0; - int result = 0; - if(containsNull && filter.test(keys[nullIndex])) result++; - for(int i = nullIndex-1;i>=0;i--) { - if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) result++; - } - return result; - } - - private void ensureCapacity(int newCapacity) { - int size = HashUtil.arraySize(newCapacity, loadFactor); - if(size > nullIndex) rehash(size); - } - - protected boolean removeIndex(int pos) { - if(pos == nullIndex) return containsNull ? removeNullIndex() : false; - keys[pos] = EMPTY_KEY_VALUE; - size--; - onNodeRemoved(pos); - shiftKeys(pos); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return true; - } - - protected boolean removeNullIndex() { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - size--; - onNodeRemoved(nullIndex); - if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); - return true; - } - - protected void onNodeAdded(int pos) { - - } - - protected void onNodeRemoved(int pos) { - - } - - protected void onNodeMoved(int from, int to) { - - } - - protected void shiftKeys(int startPos) { - int slot, last; - KEY_TYPE current; - while(true) { - startPos = ((last = startPos) + 1) & mask; - while(true){ - if(KEY_EQUALS_NULL((current = keys[startPos]))) { - keys[last] = EMPTY_KEY_VALUE; - return; - } - slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; - if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; - startPos = ++startPos & mask; - } - keys[last] = current; - onNodeMoved(startPos, last); - } - } - - protected void rehash(int newSize) { - int newMask = newSize - 1; - KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); - for(int i = nullIndex, pos = 0, j = (size - (containsNull ? 1 : 0));j-- != 0;) { - while(true) { - if(--i < 0) throw new ConcurrentModificationException("Set was modified during rehash"); - if(KEY_EQUALS_NOT_NULL(keys[i])) break; - } - if(KEY_EQUALS_NOT_NULL(newKeys[pos = HashUtil.mix(KEY_TO_HASH(keys[i])) & newMask])) - while(KEY_EQUALS_NOT_NULL(newKeys[pos = (++pos & newMask)])); - newKeys[pos] = keys[i]; - } - nullIndex = newSize; - mask = newMask; - maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); - keys = newKeys; - } - - @Override - public ITERATOR KEY_GENERIC_TYPE iterator() { - return new SetIterator(); - } - - @Override - public HASH_SET KEY_GENERIC_TYPE copy() { - HASH_SET KEY_GENERIC_TYPE set = new HASH_SETBRACES(0, loadFactor); - set.minCapacity = minCapacity; - set.mask = mask; - set.maxFill = maxFill; - set.nullIndex = nullIndex; - set.containsNull = containsNull; - set.size = size; - set.keys = Arrays.copyOf(keys, keys.length); - return set; - } - - @Override - public void clear() { - if(size == 0) return; - size = 0; - containsNull = false; - Arrays.fill(keys, EMPTY_KEY_VALUE); - } - - @Override - public int size() { - return size; - } - - private class SetIterator implements ITERATOR KEY_GENERIC_TYPE { - int pos = nullIndex; - int returnedPos = -1; - int lastReturned = -1; - int nextIndex = Integer.MIN_VALUE; - boolean returnNull = containsNull; - KEY_TYPE[] wrapped = null; - int wrappedIndex = 0; - - @Override - public boolean hasNext() { - if(nextIndex == Integer.MIN_VALUE) { - if(returnNull) { - returnNull = false; - nextIndex = nullIndex; - } - else - { - 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; - } - - @Override - public KEY_TYPE NEXT() { - if(!hasNext()) throw new NoSuchElementException(); - returnedPos = pos; - if(nextIndex < 0){ - lastReturned = Integer.MAX_VALUE; - KEY_TYPE value = wrapped[nextIndex]; - nextIndex = Integer.MIN_VALUE; - return value; - } - KEY_TYPE value = keys[(lastReturned = nextIndex)]; - nextIndex = Integer.MIN_VALUE; - return value; - } - - @Override - public void remove() { - if(lastReturned == -1) throw new IllegalStateException(); - if(lastReturned == nullIndex) { - containsNull = false; - keys[nullIndex] = EMPTY_KEY_VALUE; - } - else if(returnedPos >= 0) shiftKeys(returnedPos); - else { - HASH_SET.this.remove(wrapped[-returnedPos - 1]); - lastReturned = -1; - return; - } - size--; - lastReturned = -1; - } - - private void shiftKeys(int startPos) { - int slot, last; - KEY_TYPE current; - while(true) { - startPos = ((last = startPos) + 1) & mask; - while(true){ - if(KEY_EQUALS_NULL((current = keys[startPos]))) { - keys[last] = EMPTY_KEY_VALUE; - return; - } - slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; - if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; - startPos = ++startPos & mask; - } - if(startPos < last) addWrapper(keys[startPos]); - keys[last] = current; - } - } - - private void addWrapper(KEY_TYPE value) { - if(wrapped == null) wrapped = NEW_KEY_ARRAY(2); - else if(wrappedIndex >= wrapped.length) { - KEY_TYPE[] newArray = NEW_KEY_ARRAY(wrapped.length * 2); - System.arraycopy(wrapped, 0, newArray, 0, wrapped.length); - wrapped = newArray; - } - wrapped[wrappedIndex++] = value; - } - } +package speiger.src.collections.PACKAGE.sets; + +import java.util.Arrays; +import java.util.Collection; +import java.util.ConcurrentModificationException; +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Objects; +#if TYPE_OBJECT +import java.util.function.Consumer; +import java.util.function.BiFunction; +#endif +#if JDK_FUNCTION +import java.util.function.PREDICATE; +#endif + +import speiger.src.collections.PACKAGE.collections.COLLECTION; +import speiger.src.collections.PACKAGE.collections.ITERATOR; +#if !TYPE_OBJECT +import speiger.src.collections.PACKAGE.utils.ITERATORS; +import speiger.src.collections.PACKAGE.functions.CONSUMER; +#endif +import speiger.src.collections.ints.functions.consumer.BI_FROM_INT_CONSUMER; +import speiger.src.collections.objects.functions.consumer.BI_FROM_OBJECT_CONSUMER; +#if !JDK_FUNCTION +import speiger.src.collections.PACKAGE.functions.function.PREDICATE; +#endif +import speiger.src.collections.PACKAGE.functions.function.UNARY_OPERATOR; +import speiger.src.collections.objects.utils.ObjectArrays; + +import speiger.src.collections.utils.HashUtil; +import speiger.src.collections.utils.ITrimmable; +import speiger.src.collections.utils.SanityChecks; + +/** + * A Type Specific Custom implementation of the HashSet + * Instead of using Wrapper Object Arrays for storing keys and values there is dedicated arrays for storing keys. + * Extra to that there is a couple quality of life functions provided + * @Type(T) + */ +public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE implements ITrimmable +{ + /** The Backing keys array */ + protected transient KEY_TYPE[] keys; + /** If a null value is present */ + protected transient boolean containsNull; + /** Minimum array size the HashSet will be */ + protected transient int minCapacity; + /** Index of the Null Value */ + protected transient int nullIndex; + /** Maximum amount of Values that can be stored before the array gets expanded usually 75% */ + protected transient int maxFill; + /** Max Index that is allowed to be searched through nullIndex - 1 */ + protected transient int mask; + + /** Amount of Elements stored in the HashSet */ + protected int size; + /** How full the Array is allowed to get before resize */ + protected final float loadFactor; + + /** + * Default Constructor + */ + public HASH_SET() { + this(HashUtil.DEFAULT_MIN_CAPACITY, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Constructor that defines the minimum capacity + * @param minCapacity the minimum capacity the HashSet is allowed to be. + * @throws IllegalStateException if the minimum capacity is negative + */ + public HASH_SET(int minCapacity) { + this(minCapacity, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Constructor that defines the minimum capacity and load factor + * @param minCapacity the minimum capacity the HashSet 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 HASH_SET(int minCapacity, float loadFactor) { + if(minCapacity < 0) throw new IllegalStateException("Minimum Capacity is negative. This is not allowed"); + if(loadFactor <= 0 || loadFactor >= 1F) throw new IllegalStateException("Load Factor is not between 0 and 1"); + this.loadFactor = loadFactor; + this.minCapacity = nullIndex = HashUtil.arraySize(minCapacity, loadFactor); + mask = nullIndex - 1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = NEW_KEY_ARRAY(nullIndex + 1); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + */ + public HASH_SET(KEY_TYPE[] array) { + this(array, 0, array.length, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @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 HASH_SET(KEY_TYPE[] array, float loadFactor) { + this(array, 0, array.length, loadFactor); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public HASH_SET(KEY_TYPE[] array, int offset, int length) { + this(array, offset, length, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * Helper constructor that allow to create a set from unboxed values + * @param array the elements that should be put into the set + * @param offset the starting index within the array that should be used + * @param length the amount of elements used from the array + * @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 + * @throws IllegalStateException if offset and length causes to step outside of the arrays range + */ + public HASH_SET(KEY_TYPE[] array, int offset, int length, float loadFactor) { + this(length < 0 ? 0 : length); + SanityChecks.checkArrayCapacity(array.length, offset, length); + for(int i = 0;i collection) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @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 + */ + @Primitive + public HASH_SET(Collection collection, float loadFactor) { + this(collection.size(), loadFactor); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + */ + public HASH_SET(COLLECTION KEY_GENERIC_TYPE collection) { + this(collection, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a Set with exactly the same values as the provided collection. + * @param collection the set the elements should be added to the Set + * @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 HASH_SET(COLLECTION KEY_GENERIC_TYPE collection, float loadFactor) { + this(collection.size()); + addAll(collection); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + */ + public HASH_SET(Iterator iterator) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @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 HASH_SET(Iterator iterator, float loadFactor) { +#if !TYPE_OBJECT + this(ITERATORS.wrap(iterator), loadFactor); +#else + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); + while(iterator.hasNext()) add(iterator.next()); +#endif + } + +#if !TYPE_OBJECT + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + */ + public HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator) { + this(iterator, HashUtil.DEFAULT_LOAD_FACTOR); + } + + /** + * A Helper constructor that allows to create a set from a iterator of an unknown size + * @param iterator the elements that should be added to the set + * @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 HASH_SET(ITERATOR KEY_GENERIC_TYPE iterator, float loadFactor) { + this(HashUtil.DEFAULT_MIN_CAPACITY, loadFactor); + while(iterator.hasNext()) add(iterator.NEXT()); + } + +#endif + @Override + public boolean add(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) { + if(containsNull) return false; + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NOT_NULL(current)) { + if(KEY_EQUALS(current, o)) return false; + while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) + if(KEY_EQUALS(current, o)) return false; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return true; + } + +#if TYPE_OBJECT + @Override + public KEY_TYPE addOrGet(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) { + if(containsNull) return null; + containsNull = true; + onNodeAdded(nullIndex); + } + else { + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NOT_NULL(current)) { + if(KEY_EQUALS(current, o)) return current; + while(KEY_EQUALS_NOT_NULL((current = keys[pos = (++pos & mask)]))) + if(KEY_EQUALS(current, o)) return current; + } + keys[pos] = o; + onNodeAdded(pos); + } + if(size++ >= maxFill) rehash(HashUtil.arraySize(size+1, loadFactor)); + return o; + } + +#endif + @Override + @Primitive + public boolean addAll(Collection c) { + if(loadFactor <= 0.5F) ensureCapacity(c.size()); + else ensureCapacity(c.size() + size()); + return super.addAll(c); + } + + @Override + public boolean addAll(COLLECTION KEY_GENERIC_TYPE c) { + if(loadFactor <= 0.5F) ensureCapacity(c.size()); + else ensureCapacity(c.size() + size()); + return super.addAll(c); + } + + @Override + public boolean contains(Object o) { +#if TYPE_OBJECT + if(o == null) return containsNull; +#else + if(o == null) return false; + if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return containsNull; +#endif + int pos = HashUtil.mix(o.hashCode()) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(EQUALS_KEY_TYPE(current, o)) return true; + while(true) { + if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; + else if(EQUALS_KEY_TYPE(current, o)) return true; + } + } + + @Override + public boolean remove(Object o) { +#if TYPE_OBJECT + if(o == null) return (containsNull ? removeNullIndex() : false); +#else + if(o == null) return false; + if(o instanceof CLASS_TYPE && KEY_EQUALS(CLASS_TO_KEY(o), EMPTY_KEY_VALUE)) return (containsNull ? removeNullIndex() : false); +#endif + int pos = HashUtil.mix(o.hashCode()) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(EQUALS_KEY_TYPE(current, o)) return removeIndex(pos); + while(true) { + if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; + else if(EQUALS_KEY_TYPE(current, o)) return removeIndex(pos); + } + } + +#if !TYPE_OBJECT + @Override + public boolean contains(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) return containsNull; + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(KEY_EQUALS(current, o)) return true; + while(true) { + if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; + else if(KEY_EQUALS(current, o)) return true; + } + } + + @Override + public boolean remove(KEY_TYPE o) { + if(KEY_EQUALS_NULL(o)) return containsNull ? removeNullIndex() : false; + int pos = HashUtil.mix(KEY_TO_HASH(o)) & mask; + KEY_TYPE current = keys[pos]; + if(KEY_EQUALS_NULL(current)) return false; + if(KEY_EQUALS(current, o)) return removeIndex(pos); + while(true) { + if(KEY_EQUALS_NULL((current = keys[pos = (++pos & mask)]))) return false; + else if(KEY_EQUALS(current, o)) return removeIndex(pos); + } + } + +#endif + @Override + public boolean trim(int size) { + int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); + if(request >= nullIndex || this.size >= Math.min((int)Math.ceil(request * loadFactor), request - 1)) return false; + try { + rehash(request); + } + catch(OutOfMemoryError e) { return false; } + return true; + } + + @Override + public void clearAndTrim(int size) { + int request = Math.max(minCapacity, HashUtil.nextPowerOfTwo((int)Math.ceil(size / loadFactor))); + if(request >= nullIndex) { + clear(); + return; + } + nullIndex = request; + mask = request-1; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = NEW_KEY_ARRAY(request + 1); + this.size = 0; + containsNull = false; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { + if(a == null || a.length < size()) a = new KEY_TYPE[size()]; + for(int i = nullIndex-1, index = 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) a[index++] = keys[i]; + } + if (a.length > size) a[size] = EMPTY_KEY_VALUE; + return a; + } + +#endif + @Override + @Deprecated + public Object[] toArray() { + if(isEmpty()) return ObjectArrays.EMPTY_ARRAY; + Object[] obj = new Object[size()]; + for(int i = nullIndex-1, index = 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) obj[index++] = KEY_TO_OBJ(keys[i]); + } + return obj; + } + + @Override + @Primitive + public E[] toArray(E[] a) { + if(a == null) a = (E[])new Object[size()]; + else if(a.length < size()) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size()); + for(int i = nullIndex-1, index = 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) a[index++] = (E)KEY_TO_OBJ(keys[i]); + } + if (a.length > size) a[size] = null; + return a; + } + + @Override + public void forEach(CONSUMER KEY_SUPER_GENERIC_TYPE action) { + if(size() <= 0) return; + if(containsNull) action.accept(keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(keys[i]); + } + } + + @Override + public void forEachIndexed(BI_FROM_INT_CONSUMER KEY_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + if(containsNull) action.accept(0, keys[nullIndex]); + for(int i = nullIndex-1, index = containsNull ? 1 : 0;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(index++, keys[i]); + } + } + + @Override + public void forEach(E input, BI_FROM_OBJECT_CONSUMER KSK_GENERIC_TYPE action) { + Objects.requireNonNull(action); + if(size() <= 0) return; + if(containsNull) action.accept(input, keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i])) action.accept(input, keys[i]); + } + } + + @Override + public boolean matchesAny(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return false; + if(containsNull && filter.test(keys[nullIndex])) return true; + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) return true; + } + return false; + } + + @Override + public boolean matchesNone(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + if(containsNull && filter.test(keys[nullIndex])) return false; + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) return false; + } + return true; + } + + @Override + public boolean matchesAll(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return true; + if(containsNull && !filter.test(keys[nullIndex])) return false; + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && !filter.test(keys[i])) return false; + } + return true; + } + +#if !TYPE_OBJECT + @Override + public KEY_TYPE reduce(KEY_TYPE identity, UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = identity; + if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.APPLY_VALUE(state, keys[i]); + } + return state; + } + +#else + @Override + public KEY_SPECIAL_TYPE reduce(KEY_SPECIAL_TYPE identity, BiFunction operator) { + Objects.requireNonNull(operator); + KEY_SPECIAL_TYPE state = identity; + if(containsNull) state = operator.APPLY_VALUE(state, keys[nullIndex]); + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + state = operator.APPLY_VALUE(state, keys[i]); + } + return state; + } + +#endif + @Override + public KEY_TYPE reduce(UNARY_OPERATOR KEY_KEY_GENERIC_TYPE operator) { + Objects.requireNonNull(operator); + KEY_TYPE state = EMPTY_VALUE; + boolean empty = true; + if(containsNull) { + state = keys[nullIndex]; + empty = false; + } + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NULL(keys[i])) continue; + if(empty) { + empty = false; + state = keys[i]; + continue; + } + state = operator.APPLY_VALUE(state, keys[i]); + } + return state; + } + + @Override + public KEY_TYPE findFirst(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return EMPTY_VALUE; + if(containsNull && filter.test(keys[nullIndex])) return keys[nullIndex]; + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) return keys[i]; + } + return EMPTY_VALUE; + } + + @Override + public int count(PREDICATE KEY_GENERIC_TYPE filter) { + Objects.requireNonNull(filter); + if(size() <= 0) return 0; + int result = 0; + if(containsNull && filter.test(keys[nullIndex])) result++; + for(int i = nullIndex-1;i>=0;i--) { + if(KEY_EQUALS_NOT_NULL(keys[i]) && filter.test(keys[i])) result++; + } + return result; + } + + private void ensureCapacity(int newCapacity) { + int size = HashUtil.arraySize(newCapacity, loadFactor); + if(size > nullIndex) rehash(size); + } + + protected boolean removeIndex(int pos) { + if(pos == nullIndex) return containsNull ? removeNullIndex() : false; + keys[pos] = EMPTY_KEY_VALUE; + size--; + onNodeRemoved(pos); + shiftKeys(pos); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return true; + } + + protected boolean removeNullIndex() { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + size--; + onNodeRemoved(nullIndex); + if(nullIndex > minCapacity && size < maxFill / 4 && nullIndex > HashUtil.DEFAULT_MIN_CAPACITY) rehash(nullIndex / 2); + return true; + } + + protected void onNodeAdded(int pos) { + + } + + protected void onNodeRemoved(int pos) { + + } + + protected void onNodeMoved(int from, int to) { + + } + + protected void shiftKeys(int startPos) { + int slot, last; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(KEY_EQUALS_NULL((current = keys[startPos]))) { + keys[last] = EMPTY_KEY_VALUE; + return; + } + slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + keys[last] = current; + onNodeMoved(startPos, last); + } + } + + protected void rehash(int newSize) { + int newMask = newSize - 1; + KEY_TYPE[] newKeys = NEW_KEY_ARRAY(newSize + 1); + for(int i = nullIndex, pos = 0, j = (size - (containsNull ? 1 : 0));j-- != 0;) { + while(true) { + if(--i < 0) throw new ConcurrentModificationException("Set was modified during rehash"); + if(KEY_EQUALS_NOT_NULL(keys[i])) break; + } + if(KEY_EQUALS_NOT_NULL(newKeys[pos = HashUtil.mix(KEY_TO_HASH(keys[i])) & newMask])) + while(KEY_EQUALS_NOT_NULL(newKeys[pos = (++pos & newMask)])); + newKeys[pos] = keys[i]; + } + nullIndex = newSize; + mask = newMask; + maxFill = Math.min((int)Math.ceil(nullIndex * loadFactor), nullIndex - 1); + keys = newKeys; + } + + @Override + public ITERATOR KEY_GENERIC_TYPE iterator() { + return new SetIterator(); + } + + @Override + public HASH_SET KEY_GENERIC_TYPE copy() { + HASH_SET KEY_GENERIC_TYPE set = new HASH_SETBRACES(0, loadFactor); + set.minCapacity = minCapacity; + set.mask = mask; + set.maxFill = maxFill; + set.nullIndex = nullIndex; + set.containsNull = containsNull; + set.size = size; + set.keys = Arrays.copyOf(keys, keys.length); + return set; + } + + @Override + public void clear() { + if(size == 0) return; + size = 0; + containsNull = false; + Arrays.fill(keys, EMPTY_KEY_VALUE); + } + + @Override + public int size() { + return size; + } + + private class SetIterator implements ITERATOR KEY_GENERIC_TYPE { + int pos = nullIndex; + int returnedPos = -1; + int lastReturned = -1; + int nextIndex = Integer.MIN_VALUE; + boolean returnNull = containsNull; + KEY_TYPE[] wrapped = null; + int wrappedIndex = 0; + + @Override + public boolean hasNext() { + if(nextIndex == Integer.MIN_VALUE) { + if(returnNull) { + returnNull = false; + nextIndex = nullIndex; + } + else + { + 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; + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + returnedPos = pos; + if(nextIndex < 0){ + lastReturned = Integer.MAX_VALUE; + KEY_TYPE value = wrapped[nextIndex]; + nextIndex = Integer.MIN_VALUE; + return value; + } + KEY_TYPE value = keys[(lastReturned = nextIndex)]; + nextIndex = Integer.MIN_VALUE; + return value; + } + + @Override + public void remove() { + if(lastReturned == -1) throw new IllegalStateException(); + if(lastReturned == nullIndex) { + containsNull = false; + keys[nullIndex] = EMPTY_KEY_VALUE; + } + else if(returnedPos >= 0) shiftKeys(returnedPos); + else { + HASH_SET.this.remove(wrapped[-returnedPos - 1]); + lastReturned = -1; + return; + } + size--; + lastReturned = -1; + } + + private void shiftKeys(int startPos) { + int slot, last; + KEY_TYPE current; + while(true) { + startPos = ((last = startPos) + 1) & mask; + while(true){ + if(KEY_EQUALS_NULL((current = keys[startPos]))) { + keys[last] = EMPTY_KEY_VALUE; + return; + } + slot = HashUtil.mix(KEY_TO_HASH(current)) & mask; + if(last <= startPos ? (last >= slot || slot > startPos) : (last >= slot && slot > startPos)) break; + startPos = ++startPos & mask; + } + if(startPos < last) addWrapper(keys[startPos]); + keys[last] = current; + } + } + + private void addWrapper(KEY_TYPE value) { + if(wrapped == null) wrapped = NEW_KEY_ARRAY(2); + else if(wrappedIndex >= wrapped.length) { + KEY_TYPE[] newArray = NEW_KEY_ARRAY(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/utils/Stack.java b/src/main/java/speiger/src/collections/utils/Stack.java index fa878c9..6f59996 100644 --- a/src/main/java/speiger/src/collections/utils/Stack.java +++ b/src/main/java/speiger/src/collections/utils/Stack.java @@ -1,5 +1,8 @@ package speiger.src.collections.utils; +import java.util.Collection; +import java.util.NoSuchElementException; +import java.util.function.IntFunction; /** * The Stack Interface represents the Last-In-First-Out layout (LIFO). @@ -16,6 +19,14 @@ public interface Stack */ public void push(T e); + /** + * Helper function that pushes the top element on top of the stack again. + * @throws NoSuchElementException if the stack is empty + */ + public default void pushTop() { + push(top()); + } + /** * Removes the Object on top of the stack. * @return the element that is on top of the stack @@ -59,4 +70,33 @@ public interface Stack * @throws ArrayIndexOutOfBoundsException if the index is out of bounds */ public T peek(int index); + + /** + * A method to drop the contents of the Queue without clearing the queue + * @param the keyType of elements maintained by this Collection + * @return the contents of the queue into a seperate array. + */ + public default E[] toArray() { + return toArray((E[])new Object[size()]); + } + + /** + * A method to drop the contents of the Stack without clearing the stack + * @param array where the elements should be inserted to. If it does not fit then it creates a new appropriately created array + * @param the keyType of elements maintained by this Collection + * @return the contents of the stack into a separate array. + * @note if the Type is generic then a Object Array is created instead of a Type Array + */ + public E[] toArray(E[] array); + + /** + * A Helper function that simplifies the process of creating a new Array. + * @param action the array creation function + * @param the returning arrayType + * @return an array containing all of the elements in this collection + * @see Collection#toArray(Object[]) + */ + public default E[] toArray(IntFunction action) { + return toArray(action.apply(size())); + } }