diff --git a/Changelog.md b/Changelog.md index f88516fc..171dc4ec 100644 --- a/Changelog.md +++ b/Changelog.md @@ -28,6 +28,9 @@ - Fixed: CustomOpenHashMap.keySet.forEach was basically putting out keys even if they were present - Fixed: ImmutableMaps issues thanks to the tests. Roughly the same as the rest of the maps - Fixed: RB/AVLTreeMaps issues. Roughly the same as the rest of the maps +- Fixed: SubLists are now properly implemented. +- Fixed: HashSet Iterator bugs now fixed... That was Painful. +- Added: Tests for Lists and Sets ### Version 0.4.5 - Added: removeAll/retainAll(Collection c, Consumer r) which receives all the elements that got deleted from the collection diff --git a/README.md b/README.md index e6e76dba..6cee67fd 100644 --- a/README.md +++ b/README.md @@ -19,6 +19,21 @@ But its focus is a different one. - Suppliers - Bi/Consumers + +## Current Level of Stability +Since this is a relatively new Library, stability was not perfect and some areas are not perfect yet. +Thanks to @ben-manes we now have Roughly 16000 test covering Maps/Sets/Lists. +These tests cover Javas Collection API completely and ensuring a Stable implementation. +These freshly added tests allowed me to squash thousands of issues according to Googles Test Library (Guava-Tests). +These will be expanded on as time goes on. + +One know aspect of Instability is SubSets and SubMaps. They require full rewrites to be fully stable. +So it is not advised to use them until these issues are addressed. +PriorityQueues are tested separately and ensure basic functionality though GuavaTests are planned. +They just require a custom implementation to run. + +As a summary: Stability is good/excellent, unless you need SubSets/SubMaps + ## Specialized Functions New Specialized functions that were added to increase performance or reduce allocations or Quality Of life. To highlight things that may be wanted. @@ -121,5 +136,5 @@ to build the jar: do not combine the commands because they can not be executed at the same time. ## Current Down Sides (Random order) -- Testing for Sub Maps/Sets/Lists are only in a very basic way tested +- SubMaps/Set implementation isn't perfect right now. This will be addressed slowly due to rewrites required. - Documentation is only present at the lowest level for most cases and needs a typo fixing. \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template b/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template index f0076c9e..38026770 100644 --- a/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template +++ b/src/builder/resources/speiger/assets/collections/templates/lists/AbstractList.template @@ -3,11 +3,15 @@ package speiger.src.collections.PACKAGE.lists; import java.util.Collection; import java.util.List; import java.util.ListIterator; +import java.util.NoSuchElementException; import java.util.Objects; import speiger.src.collections.PACKAGE.collections.ABSTRACT_COLLECTION; import speiger.src.collections.PACKAGE.collections.COLLECTION; import speiger.src.collections.PACKAGE.collections.ITERATOR; +import speiger.src.collections.PACKAGE.collections.SPLIT_ITERATOR; +import speiger.src.collections.PACKAGE.utils.SPLIT_ITERATORS; +import speiger.src.collections.utils.SanityChecks; /** * Abstract implementation of the {@link LIST} interface. @@ -15,9 +19,8 @@ import speiger.src.collections.PACKAGE.collections.ITERATOR; */ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION KEY_GENERIC_TYPE implements LIST KEY_GENERIC_TYPE { -#if !TYPE_OBJECT /** - * A Type-Specific implementation of add function that delegates to {@link #add(int, KEY_TYPE)} + * A Type-Specific implementation of add function that delegates to {@link List#add(int, Object)} */ @Override public boolean add(KEY_TYPE e) { @@ -25,6 +28,7 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION return true; } +#if !TYPE_OBJECT /** {@inheritDoc} *

This default implementation delegates to the corresponding type-specific function. * @deprecated Please use the corresponding type-specific function instead. @@ -213,7 +217,8 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public LIST KEY_GENERIC_TYPE subList(int fromIndex, int toIndex) { - return new SUB_LIST(this, fromIndex, toIndex); + SanityChecks.checkArrayCapacity(size(), fromIndex, toIndex-fromIndex); + return new SubList(this, 0, fromIndex, toIndex); } @Override @@ -228,6 +233,7 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public LIST_ITERATOR KEY_GENERIC_TYPE listIterator(int index) { + if(index < 0 || index > size()) throw new IndexOutOfBoundsException(); return new LIST_ITER(index); } @@ -239,134 +245,128 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION public ABSTRACT_LIST KEY_GENERIC_TYPE copy() { throw new UnsupportedOperationException(); } - private class SUB_LIST extends ABSTRACT_LIST KEY_GENERIC_TYPE { - ABSTRACT_LIST KEY_GENERIC_TYPE l; - int offset; + private class SubList extends ABSTRACT_LIST KEY_GENERIC_TYPE + { + final ABSTRACT_LIST KEY_GENERIC_TYPE list; + final int parentOffset; + final int offset; int size; - SUB_LIST(ABSTRACT_LIST KEY_GENERIC_TYPE l, int from, int to) { - if (from < 0) throw new IndexOutOfBoundsException("fromIndex = " + from); - else if (to > l.size()) throw new IndexOutOfBoundsException("toIndex = " + to); - else if (from > to) throw new IllegalArgumentException("fromIndex(" + from + ") > toIndex(" + to + ")"); - this.l = l; - offset = from; - size = to - from; + public SubList(ABSTRACT_LIST KEY_GENERIC_TYPE list, int offset, int from, int to) { + this.list = list; + this.parentOffset = from; + this.offset = offset + from; + this.size = to - from; } @Override - public void add(int index, KEY_TYPE e) { - checkAddRange(index); - l.add(index+offset, e); + public void add(int index, KEY_TYPE element) { + checkAddSubRange(index); + list.add(parentOffset+index, element); size++; } @Override public boolean addAll(int index, Collection c) { - checkAddRange(index); - int size = c.size(); - if(size == 0) return false; - l.addAll(index + offset, l); - offset += size; + checkAddSubRange(index); + int add = c.size(); + if(add <= 0) return false; + list.addAll(parentOffset+index, c); + this.size += add; return true; } @Override public boolean addAll(int index, COLLECTION KEY_GENERIC_TYPE c) { - checkAddRange(index); - int size = c.size(); - if(size == 0) return false; - l.addAll(index + offset, l); - offset += size; + checkAddSubRange(index); + int add = c.size(); + if(add <= 0) return false; + list.addAll(parentOffset+index, c); + this.size += add; return true; } - + @Override public boolean addAll(int index, LIST KEY_GENERIC_TYPE c) { - checkAddRange(index); - int size = c.size(); - if(size == 0) return false; - l.addAll(index + offset, l); - offset += size; - return true; - } - - @Override - public boolean addAll(KEY_TYPE[] e, int offset, int length) { - if(length <= 0) return false; - l.addElements(this.offset, e, offset, length); - offset += length; + checkAddSubRange(index); + int add = c.size(); + if(add <= 0) return false; + list.addAll(parentOffset+index, c); + this.size += add; return true; } @Override public void addElements(int from, KEY_TYPE[] a, int offset, int length) { - checkRange(from); - l.addElements(from + this.offset, a, offset, length); - size += length; + checkAddSubRange(from); + if(length <= 0) return; + list.addElements(parentOffset+from, a, offset, length); + this.size += length; } @Override public KEY_TYPE[] getElements(int from, KEY_TYPE[] a, int offset, int length) { - checkRange(from); - return l.getElements(from + this.offset, a, offset, length); + SanityChecks.checkArrayCapacity(size, from, length); + SanityChecks.checkArrayCapacity(a.length, offset, length); + return list.getElements(from+this.offset, a, offset, length); } @Override public void removeElements(int from, int to) { - checkRange(from); - checkRange(to); - l.removeElements(from + offset, to + offset); + if(to-from <= 0) return; + checkSubRange(from); + checkSubRange(to); + list.removeElements(from+parentOffset, to+parentOffset); size -= to - from; } #if TYPE_OBJECT @Override - public K[] extractElements(int from, int to, Class clz) { - checkRange(from); - checkRange(to); - K[] a = l.extractElements(from + offset, to + offset, clz); + public K[] extractElements(int from, int to, Class type) { + checkSubRange(from); + checkSubRange(to); + K[] result = list.extractElements(from+parentOffset, to+parentOffset, type); size -= to - from; - return a; + return result; } - + #else @Override public KEY_TYPE[] extractElements(int from, int to) { - checkRange(from); - checkRange(to); - KEY_TYPE[] a = l.extractElements(from + offset, to + offset); + checkSubRange(from); + checkSubRange(to); + KEY_TYPE[] result = list.extractElements(from+parentOffset, to+parentOffset); size -= to - from; - return a; + return result; } #endif @Override public KEY_TYPE GET_KEY(int index) { - checkRange(index); - return l.GET_KEY(index + offset); + checkSubRange(index); + return list.GET_KEY(offset+index); + } + + @Override + public KEY_TYPE set(int index, KEY_TYPE element) { + checkSubRange(index); + return list.set(offset+index, element); } @Override - public KEY_TYPE set(int index, KEY_TYPE e) { - checkRange(index); - return l.set(index + offset, e); + public KEY_TYPE swapRemove(int index) { + checkSubRange(index); + KEY_TYPE result = list.swapRemove(index+parentOffset); + size--; + return result; } @Override public KEY_TYPE REMOVE(int index) { - checkRange(index); - int temp = l.size(); - KEY_TYPE type = l.REMOVE(index + offset); - if(l.size() != temp) size--; - return type; - } - - public KEY_TYPE swapRemove(int index) { - checkRange(index); - int temp = l.size(); - KEY_TYPE type = l.swapRemove(index + offset); - if(l.size() != temp) size--; - return type; + checkSubRange(index); + KEY_TYPE result = list.REMOVE(index+parentOffset); + size--; + return result; } @Override @@ -374,17 +374,114 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION return size; } - private void checkRange(int index) { + @Override + public SPLIT_ITERATOR KEY_GENERIC_TYPE spliterator() { return SPLIT_ITERATORS.createSplititerator(this, 16464); } + + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE listIterator(int index) { + if(index < 0 || index > size()) throw new IndexOutOfBoundsException(); + return new SubListIterator(this, index); + } + + @Override + public LIST KEY_GENERIC_TYPE subList(int fromIndex, int toIndex) { + SanityChecks.checkArrayCapacity(size, fromIndex, toIndex-fromIndex); + return new SubList(list, offset, fromIndex, toIndex); + } + + protected void checkSubRange(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } - private void checkAddRange(int index) { + protected void checkAddSubRange(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } + + private class SubListIterator implements LIST_ITERATOR KEY_GENERIC_TYPE + { + ABSTRACT_LIST KEY_GENERIC_TYPE list; + int index; + int lastReturned = -1; + + SubListIterator(ABSTRACT_LIST KEY_GENERIC_TYPE list, int index) { + this.list = list; + this.index = index; + } + + @Override + public boolean hasNext() { + return index < list.size(); + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + int i = index++; + return list.GET_KEY((lastReturned = i)); + } + + @Override + public boolean hasPrevious() { + return index > 0; + } + + @Override + public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); + index--; + return list.GET_KEY((lastReturned = index)); + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public int previousIndex() { + return index-1; + } + + @Override + public void remove() { + if(lastReturned == -1) throw new IllegalStateException(); + list.REMOVE(lastReturned); + index = lastReturned; + lastReturned = -1; + } + + @Override + public void set(KEY_TYPE e) { + if(lastReturned == -1) throw new IllegalStateException(); + list.set(lastReturned, e); + } + + @Override + public void add(KEY_TYPE e) { + list.add(index++, e); + lastReturned = -1; + } + + @Override + public int skip(int amount) { + if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed"); + int steps = Math.min(amount, (list.size() - 1) - index); + index += steps; + return steps; + } + + @Override + public int back(int amount) { + if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed"); + int steps = Math.min(amount, index); + index -= steps; + return steps; + } + } } - + private class LIST_ITER implements LIST_ITERATOR KEY_GENERIC_TYPE { int index; int lastReturned = -1; @@ -400,8 +497,9 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public KEY_TYPE NEXT() { - lastReturned = index; - return GET_KEY(index++); + if(!hasNext()) throw new NoSuchElementException(); + int i = index++; + return GET_KEY((lastReturned = i)); } @Override @@ -411,8 +509,9 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public KEY_TYPE PREVIOUS() { - lastReturned = index; - return GET_KEY(index--); + if(!hasPrevious()) throw new NoSuchElementException(); + index--; + return GET_KEY((lastReturned = index)); } @Override @@ -427,25 +526,20 @@ public abstract class ABSTRACT_LIST KEY_GENERIC_TYPE extends ABSTRACT_COLLECTION @Override public void remove() { - if(lastReturned == -1) - throw new IllegalStateException(); + if(lastReturned == -1) throw new IllegalStateException(); ABSTRACT_LIST.this.REMOVE(lastReturned); - if(lastReturned < index) - index--; + index = lastReturned; lastReturned = -1; } @Override public void set(KEY_TYPE e) { - if(lastReturned == -1) - throw new IllegalStateException(); + if(lastReturned == -1) throw new IllegalStateException(); ABSTRACT_LIST.this.set(lastReturned, e); } @Override public void add(KEY_TYPE e) { - if(lastReturned == -1) - throw new IllegalStateException(); ABSTRACT_LIST.this.add(index++, e); lastReturned = -1; } diff --git a/src/builder/resources/speiger/assets/collections/templates/lists/ArrayList.template b/src/builder/resources/speiger/assets/collections/templates/lists/ArrayList.template index 1c03df26..dcfc7c8f 100644 --- a/src/builder/resources/speiger/assets/collections/templates/lists/ArrayList.template +++ b/src/builder/resources/speiger/assets/collections/templates/lists/ArrayList.template @@ -1029,6 +1029,7 @@ public class ARRAY_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE for(int i = 0;i size) a[size] = null; return a; } @@ -1037,6 +1038,7 @@ public class ARRAY_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { if(a.length < size) a = new KEY_TYPE[size]; System.arraycopy(data, 0, a, 0, size); + if (a.length > size) a[size] = EMPTY_KEY_VALUE; return a; } @@ -1138,6 +1140,7 @@ public class ARRAY_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size); } + #if PRIMITIVES /** * Returns a Java-Type-Specific Stream to reduce boxing/unboxing. diff --git a/src/builder/resources/speiger/assets/collections/templates/lists/ImmutableList.template b/src/builder/resources/speiger/assets/collections/templates/lists/ImmutableList.template index 2d034332..62dd7d66 100644 --- a/src/builder/resources/speiger/assets/collections/templates/lists/ImmutableList.template +++ b/src/builder/resources/speiger/assets/collections/templates/lists/ImmutableList.template @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.Comparator; #endif import java.util.Collection; +import java.util.NoSuchElementException; import java.util.Objects; #if TYPE_OBJECT import java.util.function.BiFunction; @@ -381,6 +382,12 @@ public class IMMUTABLE_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_T return result; } + @Override + public LIST_ITERATOR KEY_GENERIC_TYPE listIterator(int index) { + if(index < 0 || index > size()) throw new IndexOutOfBoundsException(); + return new LIST_ITER(index); + } + @Override public KEY_TYPE set(int index, KEY_TYPE e) { throw new UnsupportedOperationException(); } @Override @@ -447,6 +454,7 @@ public class IMMUTABLE_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_T else if(a.length < data.length) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), data.length); for(int i = 0,m=data.length;i data.length) a[data.length] = null; return a; } @@ -455,6 +463,7 @@ public class IMMUTABLE_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_T public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { if(a.length < data.length) a = new KEY_TYPE[data.length]; System.arraycopy(data, 0, a, 0, data.length); + if (a.length > data.length) a[data.length] = EMPTY_KEY_VALUE; return a; } @@ -495,4 +504,69 @@ public class IMMUTABLE_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_T */ @Override public SPLIT_ITERATOR KEY_GENERIC_TYPE spliterator() { return SPLIT_ITERATORS.createArraySplititerator(data, data.length, 16464); } + + private class LIST_ITER implements LIST_ITERATOR KEY_GENERIC_TYPE { + int index; + + LIST_ITER(int index) { + this.index = index; + } + + @Override + public boolean hasNext() { + return index < size(); + } + + @Override + public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); + return GET_KEY(index++); + } + + @Override + public boolean hasPrevious() { + return index > 0; + } + + @Override + public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); + return GET_KEY(--index); + } + + @Override + public int nextIndex() { + return index; + } + + @Override + public int previousIndex() { + return index-1; + } + + @Override + public void remove() { throw new UnsupportedOperationException(); } + + @Override + public void set(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public void add(KEY_TYPE e) { throw new UnsupportedOperationException(); } + + @Override + public int skip(int amount) { + if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed"); + int steps = Math.min(amount, (size() - 1) - index); + index += steps; + return steps; + } + + @Override + public int back(int amount) { + if(amount < 0) throw new IllegalStateException("Negative Numbers are not allowed"); + int steps = Math.min(amount, index); + index -= steps; + return steps; + } + } } \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/lists/LinkedList.template b/src/builder/resources/speiger/assets/collections/templates/lists/LinkedList.template index bfd81e53..1f77b1d9 100644 --- a/src/builder/resources/speiger/assets/collections/templates/lists/LinkedList.template +++ b/src/builder/resources/speiger/assets/collections/templates/lists/LinkedList.template @@ -9,6 +9,7 @@ import java.nio.JAVA_BUFFER; import java.util.Collection; import java.util.Iterator; import java.util.Objects; +import java.util.NoSuchElementException; import java.util.Spliterator; #if PRIMITIVES import java.util.Spliterator.JAVA_SPLIT_ITERATOR; @@ -326,9 +327,8 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override @Primitive public int indexOf(Object o) { - if(o == null) return -1; Entry KEY_GENERIC_TYPE entry = first; - for(int i = 0;entry != null;entry = entry.next) { + for(int i = 0;entry != null;entry = entry.next,i++) { if(Objects.equals(KEY_TO_OBJ(entry.value), o)) return i; } return -1; @@ -337,9 +337,8 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override @Primitive public int lastIndexOf(Object o) { - if(o == null) return -1; Entry KEY_GENERIC_TYPE entry = last; - for(int i = size-1;entry != null;entry = entry.prev) { + for(int i = size-1;entry != null;entry = entry.prev,i--) { if(Objects.equals(KEY_TO_OBJ(entry.value), o)) return i; } return -1; @@ -354,7 +353,7 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public int indexOf(KEY_TYPE e) { Entry entry = first; - for(int i = 0;entry != null;entry = entry.next) { + for(int i = 0;entry != null;entry = entry.next,i++) { if(KEY_EQUALS(entry.value, e)) return i; } return -1; @@ -363,26 +362,17 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public int lastIndexOf(KEY_TYPE e) { Entry entry = last; - for(int i = size-1;entry != null;entry = entry.prev) { + for(int i = size-1;entry != null;entry = entry.prev,i--) { if(KEY_EQUALS(entry.value, e)) return i; } return -1; } #endif - @Override - public ITERATOR KEY_GENERIC_TYPE iterator() { - return new ListIter(first, 0); - } - - @Override - public LIST_ITERATOR KEY_GENERIC_TYPE listIterator() { - return new ListIter(first, 0); - } - @Override public LIST_ITERATOR KEY_GENERIC_TYPE listIterator(int index) { - if(index == size-1) return new ListIter(last, index); + if(index < 0 || index > size()) throw new IndexOutOfBoundsException(); + if(index == size) return new ListIter(null, index); if(index == 0) return new ListIter(first, index); return new ListIter(getNode(index), index); } @@ -564,7 +554,7 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public boolean removeFirst(KEY_TYPE e) { if(size == 0) return false; - for(Entry KEY_GENERIC_TYPE entry = first;entry.next != null;entry = entry.next) { + for(Entry KEY_GENERIC_TYPE entry = first;entry != null;entry = entry.next) { if(KEY_EQUALS(entry.value, e)) { unlink(entry); return true; @@ -576,7 +566,7 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public boolean removeLast(KEY_TYPE e) { if(size == 0) return false; - for(Entry KEY_GENERIC_TYPE entry = last;entry.prev != null;entry = entry.prev) { + for(Entry KEY_GENERIC_TYPE entry = last;entry != null;entry = entry.prev) { if(KEY_EQUALS(entry.value, e)) { unlink(entry); return true; @@ -615,7 +605,7 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public boolean REMOVE_SWAP(KEY_TYPE e) { if(size == 0) return false; - for(Entry KEY_GENERIC_TYPE entry = last;entry.prev != null;entry = entry.prev) { + for(Entry KEY_GENERIC_TYPE entry = last;entry != null;entry = entry.prev) { if(KEY_EQUALS(entry.value, e)) { if(entry.next == null) { unlinkLast(entry); @@ -649,7 +639,7 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public boolean remove(Object e) { if(size <= 0) return false; - for(Entry KEY_GENERIC_TYPE entry = first;entry.next != null;entry = entry.next) { + for(Entry KEY_GENERIC_TYPE entry = first;entry != null;entry = entry.next) { if(KEY_EQUALS(entry.value, e)) { unlink(entry); return true; @@ -949,6 +939,7 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE for(Entry KEY_GENERIC_TYPE entry = first;entry != null;entry = entry.next) { a[i++] = (E)KEY_TO_OBJ(entry.value); } + if (a.length > size) a[size] = null; return a; } @@ -958,8 +949,9 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE if(a.length < size) a = new KEY_TYPE[size]; int i = 0; for(Entry KEY_GENERIC_TYPE entry = first;entry != null;entry = entry.next) { - a[i++] = KEY_TO_OBJ(entry.value); + a[i++] = entry.value; } + if (a.length > size) a[size] = EMPTY_KEY_VALUE; return a; } @@ -1118,23 +1110,23 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE private class ListIter implements LIST_ITERATOR KEY_GENERIC_TYPE { - Entry KEY_GENERIC_TYPE node; + Entry KEY_GENERIC_TYPE next; Entry KEY_GENERIC_TYPE lastReturned; int index; - ListIter(Entry KEY_GENERIC_TYPE node, int index) { - this.node = node; + ListIter(Entry KEY_GENERIC_TYPE next, int index) { + this.next = next; this.index = index; } @Override public boolean hasNext() { - return node != null; + return index < size; } @Override public boolean hasPrevious() { - return node != null; + return index > 0; } @Override @@ -1150,23 +1142,26 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public void remove() { if(lastReturned == null) throw new IllegalStateException(); - if(lastReturned.next == node) index--; + Entry KEY_GENERIC_TYPE lastNext = lastReturned.next; unlink(lastReturned); + if (next == lastReturned) next = lastNext; + else index--; lastReturned = null; } @Override public KEY_TYPE PREVIOUS() { - lastReturned = node; - node = node.prev; + if(!hasPrevious()) throw new NoSuchElementException(); + lastReturned = next = (next == null) ? last : next.prev; index--; return lastReturned.value; } @Override public KEY_TYPE NEXT() { - lastReturned = node; - node = node.next; + if(!hasNext()) throw new NoSuchElementException(); + lastReturned = next; + next = next.next; index++; return lastReturned.value; } @@ -1179,11 +1174,10 @@ public class LINKED_LIST KEY_GENERIC_TYPE extends ABSTRACT_LIST KEY_GENERIC_TYPE @Override public void add(KEY_TYPE e) { - if(lastReturned == null) throw new IllegalStateException(); - if(node.next == null) linkLast(e); - else linkBefore(e, node); - lastReturned = null; - index++; + lastReturned = null; + if (next == null) linkLast(e); + else linkBefore(e, next); + index++; } } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template index 591519dc..fb51db76 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/customHash/OpenCustomHashMap.template @@ -1389,6 +1389,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL private class MapIterator { int pos = nullIndex; + int returnedPos = -1; int lastReturned = -1; int nextIndex = Integer.MIN_VALUE; boolean returnNull = containsNull; @@ -1420,6 +1421,7 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL public int nextEntry() { if(!hasNext()) throw new NoSuchElementException(); + returnedPos = pos; if(nextIndex < 0){ lastReturned = Integer.MAX_VALUE; int value = findIndex(wrapped.GET_KEY(nextIndex)); @@ -1439,9 +1441,10 @@ public class CUSTOM_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VAL keys[nullIndex] = EMPTY_KEY_VALUE; values[nullIndex] = EMPTY_VALUE; } - else if(lastReturned >= 0) shiftKeys(lastReturned); + else if(returnedPos >= 0) shiftKeys(returnedPos); else { - CUSTOM_HASH_MAP.this.remove(wrapped.GET_KEY(-lastReturned - 1)); + CUSTOM_HASH_MAP.this.remove(wrapped.GET_KEY(-returnedPos - 1)); + lastReturned = -1; return; } size--; diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template index 65098fd4..d7fc9582 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/hash/OpenHashMap.template @@ -1345,6 +1345,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE private class MapIterator { int pos = nullIndex; + int returnedPos = -1; int lastReturned = -1; int nextIndex = Integer.MIN_VALUE; boolean returnNull = containsNull; @@ -1376,6 +1377,7 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE public int nextEntry() { if(!hasNext()) throw new NoSuchElementException(); + returnedPos = pos; if(nextIndex < 0){ lastReturned = Integer.MAX_VALUE; int value = findIndex(wrapped.GET_KEY(nextIndex)); @@ -1395,9 +1397,10 @@ public class HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GENE keys[nullIndex] = EMPTY_KEY_VALUE; values[nullIndex] = EMPTY_VALUE; } - else if(lastReturned >= 0) shiftKeys(lastReturned); + else if(returnedPos >= 0) shiftKeys(returnedPos); else { - HASH_MAP.this.remove(wrapped.GET_KEY(-lastReturned - 1)); + HASH_MAP.this.remove(wrapped.GET_KEY(-returnedPos - 1)); + lastReturned = -1; return; } size--; diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template index 0139838c..6b7f0b63 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/immutable/ImmutableOpenHashMap.template @@ -261,7 +261,7 @@ public class IMMUTABLE_HASH_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_ newValues[pos] = b[i]; continue; } - while(KEY_EQUALS_NOT_NULL((current = newKeys[pos = (++pos & mask)]))) { + while(KEY_EQUALS_NOT_NULL((current = newKeys[pos = (++pos & newMask)]))) { if(KEY_EQUALS(current, o)) { found = false; newValues[pos] = b[i]; diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/ArrayMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/ArrayMap.template index 903a022a..e9a40b4e 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/ArrayMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/ArrayMap.template @@ -202,8 +202,11 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { int index = findIndex(key); - if(index >= 0) insertIndex(size++, key, value); - return getDefaultReturnValue(); + if(index < 0) { + insertIndex(size++, key, value); + return getDefaultReturnValue(); + } + return values[index]; } #if VALUE_PRIMITIVES @@ -463,6 +466,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index == -1) { VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, getDefaultReturnValue()); @@ -481,6 +485,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); if(index == -1) { VALUE_TYPE newValue = mappingFunction.GET_VALUE(key); @@ -488,11 +493,18 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN insertIndex(size++, key, newValue); return newValue; } - return values[index]; + VALUE_TYPE newValue = values[index]; + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { + newValue = mappingFunction.GET_VALUE(key); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; } @Override public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { + Objects.requireNonNull(valueProvider); int index = findIndex(key); if(index == -1) { VALUE_TYPE newValue = valueProvider.VALUE_GET_KEY(); @@ -500,13 +512,20 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN insertIndex(size++, key, newValue); return newValue; } - return values[index]; + VALUE_TYPE newValue = values[index]; + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { + newValue = valueProvider.VALUE_GET_KEY(); + if(VALUE_EQUALS(newValue, getDefaultReturnValue())) return newValue; + values[index] = newValue; + } + return newValue; } @Override public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); int index = findIndex(key); - if(index == -1) return getDefaultReturnValue(); + if(index == -1 || VALUE_EQUALS(values[index], getDefaultReturnValue())) return getDefaultReturnValue(); VALUE_TYPE newValue = mappingFunction.APPLY_VALUE(key, values[index]); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { removeIndex(index); @@ -518,8 +537,12 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { + Objects.requireNonNull(mappingFunction); +#if VALUE_OBJECT + Objects.requireNonNull(value); +#endif int index = findIndex(key); - VALUE_TYPE newValue = index == -1 ? value : mappingFunction.APPLY_VALUE(values[index], value); + VALUE_TYPE newValue = index == -1 || VALUE_EQUALS(values[index], getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(values[index], value); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(index >= 0) removeIndex(index); @@ -535,7 +558,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN for(MAP.Entry KEY_VALUE_GENERIC_TYPE entry : MAPS.fastIterable(m)) { KEY_TYPE key = entry.ENTRY_KEY(); int index = findIndex(key); - VALUE_TYPE newValue = index == -1 ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(values[index], entry.ENTRY_VALUE()); + VALUE_TYPE newValue = index == -1 || VALUE_EQUALS(values[index], getDefaultReturnValue()) ? entry.ENTRY_VALUE() : mappingFunction.APPLY_VALUE(values[index], entry.ENTRY_VALUE()); if(VALUE_EQUALS(newValue, getDefaultReturnValue())) { if(index >= 0) removeIndex(index); @@ -683,21 +706,18 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN #endif protected int findIndex(Object key, Object value) { - if(key == null || value == null) return -1; for(int i = size-1;i>=0;i--) if(EQUALS_KEY_TYPE(keys[i], key) && EQUALS_VALUE_TYPE(values[i], value)) return i; return -1; } protected int findIndex(Object key) { - if(key == null) return -1; for(int i = size-1;i>=0;i--) if(EQUALS_KEY_TYPE(keys[i], key)) return i; return -1; } protected int findValue(Object value) { - if(value == null) return -1; for(int i = size-1;i>=0;i--) if(EQUALS_VALUE_TYPE(values[i], value)) return i; return -1; @@ -1069,21 +1089,18 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN #endif protected int findIndex(Object key, Object value) { - if(key == null || value == null) return -1; for(int i = length-1;i>=0;i--) if(EQUALS_KEY_TYPE(keys[offset+i], key) && EQUALS_VALUE_TYPE(values[offset+i], value)) return i+offset; return -1; } protected int findIndex(Object key) { - if(key == null) return -1; for(int i = length-1;i>=0;i--) if(EQUALS_KEY_TYPE(keys[offset+i], key)) return i+offset; return -1; } protected int findValue(Object value) { - if(value == null) return -1; for(int i = length-1;i>=0;i--) if(EQUALS_VALUE_TYPE(values[offset+i], value)) return i+offset; return -1; @@ -1268,8 +1285,14 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return SubMap.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return SubMap.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + return SubMap.this.findIndex(entry.ENTRY_KEY(), entry.ENTRY_VALUE()) >= 0; + } + else { + Map.Entry entry = (Map.Entry)o; + return ARRAY_MAP.this.findIndex(entry.getKey(), entry.getValue()) >= 0; + } } return false; } @@ -1318,7 +1341,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public boolean remove(Object o) { int oldSize = length; - remove(o); + SubMap.this.remove(o); return length != oldSize; } @@ -1329,7 +1352,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public boolean remove(KEY_TYPE o) { int oldSize = length; - remove(o); + SubMap.this.remove(o); return length != oldSize; } @@ -1737,12 +1760,14 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN } public int previousEntry() { + if(!hasPrevious()) throw new NoSuchElementException(); int returnIndex = offset+index; lastReturned = index--; return returnIndex; } public int nextEntry() { + if(!hasNext()) throw new NoSuchElementException(); int returnIndex = offset+index; lastReturned = index++; return returnIndex; @@ -1944,8 +1969,16 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return ARRAY_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return ARRAY_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + int index = ARRAY_MAP.this.findIndex(entry.ENTRY_KEY()); + if(index >= 0) return VALUE_EQUALS(entry.ENTRY_VALUE(), ARRAY_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + int index = ARRAY_MAP.this.findIndex(entry.getKey()); + if(index >= 0) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(ARRAY_MAP.this.values[index])); + } } return false; } @@ -1994,7 +2027,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public boolean remove(Object o) { int oldSize = size; - remove(o); + ARRAY_MAP.this.remove(o); return size != oldSize; } @@ -2005,7 +2038,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public boolean remove(KEY_TYPE o) { int oldSize = size; - remove(o); + ARRAY_MAP.this.remove(o); return size != oldSize; } @@ -2401,11 +2434,13 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN } public int previousEntry() { + if(!hasPrevious()) throw new NoSuchElementException(); lastReturned = index; return index--; } public int nextEntry() { + if(!hasNext()) throw new NoSuchElementException(); lastReturned = index; return index++; } @@ -2480,7 +2515,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_GEN @Override public String toString() { - return KEY_TO_STRING(keys[index]) + "->" + VALUE_TO_STRING(values[index]); + return KEY_TO_STRING(keys[index]) + "=" + VALUE_TO_STRING(values[index]); } } } \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/EnumMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/EnumMap.template index 923c26cb..04a69dc2 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/EnumMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/EnumMap.template @@ -183,6 +183,7 @@ public class ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE #endif @Override public boolean containsKey(Object key) { + if(!keyType.isInstance(key)) return false; return isSet(((T)key).ordinal()); } @@ -190,7 +191,7 @@ public class ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE @Override public boolean containsValue(Object value) { for(int i = 0;i> 6] |= (1L << index); onNodeAdded(index); + present[index >> 6] |= (1L << index); + size++; } protected void clear(int index) { size--; @@ -452,7 +488,20 @@ public class ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE @Override public boolean contains(Object o) { - if(o instanceof Map.Entry) return containsKey(((Map.Entry)o).getKey()); + if(o instanceof Map.Entry) { + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + if(!keyType.isInstance(entry.ENTRY_KEY())) return false; + int index = ((T)entry.ENTRY_KEY()).ordinal(); + if(index >= 0 && ENUM_MAP.this.isSet(index)) return VALUE_EQUALS(entry.ENTRY_VALUE(), ENUM_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + if(!keyType.isInstance(entry.getKey())) return false; + int index = ((T)entry.getKey()).ordinal(); + if(index >= 0 && ENUM_MAP.this.isSet(index)) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(ENUM_MAP.this.values[index])); + } + } return false; } @@ -548,8 +597,7 @@ public class ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE class EntryIterator extends MapIterator implements ObjectIterator { @Override public MAP.Entry KEY_VALUE_GENERIC_TYPE next() { - int index = nextEntry(); - return new BasicEntry<>(keys[index], values[index]); + return new MapEntry(nextEntry()); } } @@ -584,13 +632,74 @@ public class ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE public int nextEntry() { if(!hasNext()) throw new NoSuchElementException(); lastReturnValue = nextIndex; - return nextIndex; + nextIndex = -1; + return lastReturnValue; } public void remove() { if(lastReturnValue == -1) throw new IllegalStateException(); clear(lastReturnValue); values[lastReturnValue] = EMPTY_VALUE; + lastReturnValue = -1; + } + } + + protected class MapEntry implements MAP.Entry KEY_VALUE_GENERIC_TYPE, Map.Entry { + public int index = -1; + + public MapEntry() {} + public MapEntry(int index) { + this.index = index; + } + + @Override + public KEY_TYPE ENTRY_KEY() { + return keys[index]; + } + + @Override + public VALUE_TYPE ENTRY_VALUE() { + return values[index]; + } + + @Override + public VALUE_TYPE setValue(VALUE_TYPE value) { + VALUE_TYPE oldValue = values[index]; + values[index] = value; + return oldValue; + } + + @Override + public boolean equals(Object obj) { + if(obj instanceof Map.Entry) { + if(obj instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)obj; + return KEY_EQUALS(keys[index], entry.ENTRY_KEY()) && VALUE_EQUALS(values[index], entry.ENTRY_VALUE()); + } + Map.Entry entry = (Map.Entry)obj; + Object key = entry.getKey(); + Object value = entry.getValue(); +#if TYPE_OBJECT && VALUE_OBJECT + return KEY_EQUALS(keys[index], key) && VALUE_EQUALS(values[index], value); +#else if TYPE_OBJECT + return value instanceof CLASS_VALUE_TYPE && KEY_EQUALS(keys[index], key) && VALUE_EQUALS(values[index], CLASS_TO_VALUE(value)); +#else if VALUE_OBJECT + return key instanceof CLASS_TYPE && KEY_EQUALS(keys[index], CLASS_TO_KEY(key)) && VALUE_EQUALS(values[index], value); +#else + return key instanceof CLASS_TYPE && value instanceof CLASS_VALUE_TYPE && KEY_EQUALS(keys[index], CLASS_TO_KEY(key)) && VALUE_EQUALS(values[index], CLASS_TO_VALUE(value)); +#endif + } + return false; + } + + @Override + public int hashCode() { + return KEY_TO_HASH(keys[index]) ^ VALUE_TO_HASH(values[index]); + } + + @Override + public String toString() { + return KEY_TO_STRING(keys[index]) + "=" + VALUE_TO_STRING(values[index]); } } } diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/LinkedEnumMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/LinkedEnumMap.template index 7a0f9921..5b1cd4b9 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/LinkedEnumMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/misc/LinkedEnumMap.template @@ -109,7 +109,7 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA links = enumMap.links.clone(); size = enumMap.size; } - if(map instanceof ENUM_MAP) { + else if(map instanceof ENUM_MAP) { ENUM_MAP KEY_VALUE_GENERIC_TYPE enumMap = (ENUM_MAP KEY_VALUE_GENERIC_TYPE)map; keyType = enumMap.keyType; keys = enumMap.keys; @@ -156,7 +156,7 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA links = enumMap.links.clone(); size = enumMap.size; } - if(map instanceof ENUM_MAP) { + else if(map instanceof ENUM_MAP) { ENUM_MAP KEY_VALUE_GENERIC_TYPE enumMap = (ENUM_MAP KEY_VALUE_GENERIC_TYPE)map; keyType = enumMap.keyType; keys = enumMap.keys; @@ -519,8 +519,18 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA @Deprecated public boolean contains(Object o) { if(o instanceof Map.Entry) { - if(o instanceof MAP.Entry) return LINKED_ENUM_MAP.this.containsKey(((MAP.Entry KEY_VALUE_GENERIC_TYPE)o).ENTRY_KEY()); - return LINKED_ENUM_MAP.this.containsKey(((Map.Entry)o).getKey()); + if(o instanceof MAP.Entry) { + MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; + if(!keyType.isInstance(entry.ENTRY_KEY())) return false; + int index = ((T)entry.ENTRY_KEY()).ordinal(); + if(index >= 0 && LINKED_ENUM_MAP.this.isSet(index)) return VALUE_EQUALS(entry.ENTRY_VALUE(), LINKED_ENUM_MAP.this.values[index]); + } + else { + Map.Entry entry = (Map.Entry)o; + if(!keyType.isInstance(entry.getKey())) return false; + int index = ((T)entry.getKey()).ordinal(); + if(index >= 0 && LINKED_ENUM_MAP.this.isSet(index)) return Objects.equals(entry.getValue(), VALUE_TO_OBJ(LINKED_ENUM_MAP.this.values[index])); + } } return false; } @@ -572,7 +582,7 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA @Override public boolean remove(Object o) { int oldSize = size; - remove(o); + LINKED_ENUM_MAP.this.remove(o); return size != oldSize; } @@ -585,7 +595,7 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA @Override public boolean remove(T o) { int oldSize = size; - remove(o); + LINKED_ENUM_MAP.this.remove(o); return size != oldSize; } @@ -887,6 +897,7 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA previous = (int)(links[current] >>> 32); } else next = (int)links[current]; + size--; if(previous == -1) firstIndex = next; else links[previous] ^= ((links[previous] ^ (next & 0xFFFFFFFFL)) & 0xFFFFFFFFL); @@ -894,6 +905,8 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA if (next == -1) lastIndex = previous; else links[next] ^= ((links[next] ^ ((previous & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); values[current] = EMPTY_VALUE; + present[current >> 6] &= ~(1L << current); + current = -1; } public int previousEntry() { @@ -985,7 +998,7 @@ public class LINKED_ENUM_MAP KEY_ENUM_VALUE_GENERIC_TYPE extends ENUM_MAP KEY_VA @Override public String toString() { - return KEY_TO_STRING(keys[index]) + "->" + VALUE_TO_STRING(values[index]); + return KEY_TO_STRING(keys[index]) + "=" + VALUE_TO_STRING(values[index]); } } } \ No newline at end of file diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template index d5617823..97293231 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/AVLTreeMap.template @@ -234,7 +234,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE put(KEY_TYPE key, VALUE_TYPE value) { #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); @@ -271,7 +271,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); @@ -309,7 +309,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ @Override public VALUE_TYPE addTo(KEY_TYPE key, VALUE_TYPE value) { #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); @@ -521,7 +521,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { @@ -543,7 +543,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { @@ -564,7 +564,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { Objects.requireNonNull(valueProvider); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { @@ -585,7 +585,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue())) return getDefaultReturnValue(); @@ -602,7 +602,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); VALUE_TYPE newValue = entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(entry.value, value); @@ -899,6 +899,7 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ } } + protected void validate(KEY_TYPE k) { compare(k, k); } protected int compare(KEY_TYPE k, KEY_TYPE v) { return comparator != null ? comparator.compare(k, v) : COMPAREABLE_TO_KEY(k, v);} /** From CLR */ @@ -1532,13 +1533,13 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ if(o instanceof Map.Entry) { if(o instanceof MAP.Entry) { MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; AVL_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = m.findNode(entry.ENTRY_KEY()); if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); } else { Map.Entry entry = (Map.Entry)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; #if !TYPE_OBJECT if(!(entry.getKey() instanceof CLASS_TYPE)) return false; #endif @@ -1938,13 +1939,13 @@ public class AVL_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_ if(o instanceof Map.Entry) { if(o instanceof MAP.Entry) { MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; AVL_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = AVL_TREE_MAP.this.findNode(entry.ENTRY_KEY()); if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); } else { Map.Entry entry = (Map.Entry)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; #if !TYPE_OBJECT if(!(entry.getKey() instanceof CLASS_TYPE)) return false; #endif diff --git a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template index 763db3e3..7def1a73 100644 --- a/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template +++ b/src/builder/resources/speiger/assets/collections/templates/maps/impl/tree/RBTreeMap.template @@ -234,7 +234,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE put(KEY_TYPE key, VALUE_TYPE value) { #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); @@ -271,7 +271,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE putIfAbsent(KEY_TYPE key, VALUE_TYPE value) { #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif if(tree == null) { @@ -310,7 +310,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G @Override public VALUE_TYPE addTo(KEY_TYPE key, VALUE_TYPE value) { #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif if(tree == null) { tree = first = last = new EntryKV_BRACES(key, value, null); @@ -521,7 +521,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G public VALUE_TYPE COMPUTE(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { @@ -543,7 +543,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G public VALUE_TYPE COMPUTE_IF_ABSENT(KEY_TYPE key, FUNCTION KEY_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { @@ -564,7 +564,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G public VALUE_TYPE SUPPLY_IF_ABSENT(KEY_TYPE key, VALUE_SUPPLIER VALUE_GENERIC_TYPE valueProvider) { Objects.requireNonNull(valueProvider); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null) { @@ -585,7 +585,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G public VALUE_TYPE COMPUTE_IF_PRESENT(KEY_TYPE key, UNARY_OPERATOR KEY_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); if(entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue())) return getDefaultReturnValue(); @@ -602,7 +602,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G public VALUE_TYPE MERGE(KEY_TYPE key, VALUE_TYPE value, VALUE_UNARY_OPERATOR VALUE_VALUE_GENERIC_TYPE mappingFunction) { Objects.requireNonNull(mappingFunction); #if TYPE_OBJECT - Objects.requireNonNull(key); + validate(key); #endif Entry KEY_VALUE_GENERIC_TYPE entry = findNode(key); VALUE_TYPE newValue = entry == null || VALUE_EQUALS(entry.value, getDefaultReturnValue()) ? value : mappingFunction.APPLY_VALUE(entry.value, value); @@ -905,6 +905,7 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G } } + protected void validate(KEY_TYPE k) { compare(k, k); } protected int compare(KEY_TYPE k, KEY_TYPE v) { return comparator != null ? comparator.compare(k, v) : COMPAREABLE_TO_KEY(k, v);} protected static GENERIC_KEY_VALUE_BRACES boolean isBlack(Entry KEY_VALUE_GENERIC_TYPE p) { return p == null || p.isBlack(); } protected static GENERIC_KEY_VALUE_BRACES Entry KEY_VALUE_GENERIC_TYPE parentOf(Entry KEY_VALUE_GENERIC_TYPE p) { return (p == null ? null : p.parent); } @@ -1587,13 +1588,13 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G if(o instanceof Map.Entry) { if(o instanceof MAP.Entry) { MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; RB_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = m.findNode(entry.ENTRY_KEY()); if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); } else { Map.Entry entry = (Map.Entry)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; #if !TYPE_OBJECT if(!(entry.getKey() instanceof CLASS_TYPE)) return false; #endif @@ -1993,13 +1994,13 @@ public class RB_TREE_MAP KEY_VALUE_GENERIC_TYPE extends ABSTRACT_MAP KEY_VALUE_G if(o instanceof Map.Entry) { if(o instanceof MAP.Entry) { MAP.Entry KEY_VALUE_GENERIC_TYPE entry = (MAP.Entry KEY_VALUE_GENERIC_TYPE)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; RB_TREE_MAP.Entry KEY_VALUE_GENERIC_TYPE subEntry = RB_TREE_MAP.this.findNode(entry.ENTRY_KEY()); if(subEntry != null) return VALUE_EQUALS(entry.ENTRY_VALUE(), subEntry.value); } else { Map.Entry entry = (Map.Entry)o; - if(entry.getKey() == null) return false; + if(entry.getKey() == null && comparator() == null) return false; #if !TYPE_OBJECT if(!(entry.getKey() instanceof CLASS_TYPE)) return false; #endif diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/AVLTreeSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/AVLTreeSet.template index 744cb50f..36029657 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/AVLTreeSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/AVLTreeSet.template @@ -213,6 +213,9 @@ public class AVL_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE #endif @Override public boolean add(KEY_TYPE o) { +#if TYPE_OBJECT + validate(o); +#endif if(tree == null) { tree = first = last = new EntryBRACES(o, null); size++; @@ -242,7 +245,7 @@ public class AVL_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE } fixAfterInsertion(adding); size++; - return false; + return true; } @Override @@ -652,6 +655,7 @@ public class AVL_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE } } + protected void validate(KEY_TYPE k) { compare(k, k); } protected int compare(KEY_TYPE k, KEY_TYPE v) { return comparator != null ? comparator.compare(k, v) : COMPAREABLE_TO_KEY(k, v);} /** From CLR */ diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/ArraySet.template b/src/builder/resources/speiger/assets/collections/templates/sets/ArraySet.template index 73b50b1c..59512546 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/ArraySet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/ArraySet.template @@ -74,8 +74,8 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im * @throws NegativeArraySizeException if the length is negative */ public ARRAY_SET(KEY_TYPE[] array, int length) { - data = Arrays.copyOf(array, length); - size = length; + this(length); + addAll(array, length); } /** @@ -548,6 +548,7 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im public KEY_TYPE[] TO_ARRAY(KEY_TYPE[] a) { if(a == null || a.length < size()) return Arrays.copyOf(data, size()); System.arraycopy(data, 0, a, 0, size()); + if (a.length > size) a[size] = EMPTY_KEY_VALUE; return a; } @@ -568,6 +569,7 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im else if(a.length < size()) a = (E[])ObjectArrays.newArray(a.getClass().getComponentType(), size()); for(int i = 0;i size) a[size] = null; return a; } @@ -841,6 +843,7 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im @Override public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); lastReturned = index; return data[index++]; } @@ -852,6 +855,7 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im @Override public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); lastReturned = index; return data[index--]; } @@ -924,6 +928,7 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im @Override public KEY_TYPE NEXT() { + if(!hasNext()) throw new NoSuchElementException(); lastReturned = index; return data[index++]; } @@ -935,6 +940,7 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im @Override public KEY_TYPE PREVIOUS() { + if(!hasPrevious()) throw new NoSuchElementException(); lastReturned = index; return data[index--]; } @@ -951,11 +957,9 @@ public class ARRAY_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE im @Override public void remove() { - if(lastReturned == -1) - throw new IllegalStateException(); + if(lastReturned == -1) throw new IllegalStateException(); ARRAY_SET.this.remove(data[lastReturned]); - if(lastReturned < index) - index--; + if(lastReturned < index) index--; lastReturned = -1; } diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/ImmutableOpenHashSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/ImmutableOpenHashSet.template index b354566e..f58c11cd 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/ImmutableOpenHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/ImmutableOpenHashSet.template @@ -190,7 +190,18 @@ public class IMMUTABLE_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERI { KEY_TYPE o = a[i]; if(KEY_EQUALS_NULL(o)) { - if(!containsNull) size++; + if(!containsNull) { + size++; + if(prev != -1) { + newLinks[prev] ^= ((newLinks[prev] ^ (newSize & 0xFFFFFFFFL)) & 0xFFFFFFFFL); + newLinks[newSize] ^= ((newLinks[newSize] ^ ((prev & 0xFFFFFFFFL) << 32)) & 0xFFFFFFFF00000000L); + prev = newSize; + } + else { + prev = firstIndex = newSize; + newLinks[newSize] = -1L; + } + } containsNull = true; continue; } @@ -199,7 +210,7 @@ public class IMMUTABLE_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERI KEY_TYPE current = newKeys[pos]; if(KEY_EQUALS_NOT_NULL(current)) { if(KEY_EQUALS(current, o)) continue; - while(KEY_EQUALS_NOT_NULL((current = newKeys[pos = (++pos & mask)]))) { + while(KEY_EQUALS_NOT_NULL((current = newKeys[pos = (++pos & newMask)]))) { if(KEY_EQUALS(current, o)) { found = false; break; @@ -220,7 +231,7 @@ public class IMMUTABLE_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERI } } } - nullIndex = size; + nullIndex = newSize; mask = newMask; keys = newKeys; links = newLinks; 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 f533acdf..e4674724 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/OpenCustomHashSet.template @@ -602,6 +602,7 @@ public class CUSTOM_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_T 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; @@ -634,6 +635,7 @@ public class CUSTOM_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_T @Override public KEY_TYPE NEXT() { if(!hasNext()) throw new NoSuchElementException(); + returnedPos = pos; if(nextIndex < 0){ lastReturned = Integer.MAX_VALUE; KEY_TYPE value = wrapped.GET_KEY(nextIndex); @@ -652,9 +654,10 @@ public class CUSTOM_HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_T containsNull = false; keys[nullIndex] = EMPTY_KEY_VALUE; } - else if(pos >= 0) shiftKeys(pos); + else if(returnedPos >= 0) shiftKeys(returnedPos); else { - CUSTOM_HASH_SET.this.remove(wrapped.GET_KEY(-pos - 1)); + CUSTOM_HASH_SET.this.remove(wrapped.GET_KEY(-returnedPos - 1)); + lastReturned = -1; return; } size--; 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 61ff7fd5..390b37c6 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/OpenHashSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/OpenHashSet.template @@ -559,6 +559,7 @@ public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE imp 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; @@ -592,6 +593,7 @@ public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE imp @Override public KEY_TYPE NEXT() { if(!hasNext()) throw new NoSuchElementException(); + returnedPos = pos; if(nextIndex < 0){ lastReturned = Integer.MAX_VALUE; KEY_TYPE value = wrapped.GET_KEY(nextIndex); @@ -610,9 +612,10 @@ public class HASH_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE imp containsNull = false; keys[nullIndex] = EMPTY_KEY_VALUE; } - else if(pos >= 0) shiftKeys(pos); + else if(returnedPos >= 0) shiftKeys(returnedPos); else { - HASH_SET.this.remove(wrapped.GET_KEY(-pos - 1)); + HASH_SET.this.remove(wrapped.GET_KEY(-returnedPos - 1)); + lastReturned = -1; return; } size--; diff --git a/src/builder/resources/speiger/assets/collections/templates/sets/RBTreeSet.template b/src/builder/resources/speiger/assets/collections/templates/sets/RBTreeSet.template index ebaa06f9..83872ffd 100644 --- a/src/builder/resources/speiger/assets/collections/templates/sets/RBTreeSet.template +++ b/src/builder/resources/speiger/assets/collections/templates/sets/RBTreeSet.template @@ -213,6 +213,9 @@ public class RB_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE #endif @Override public boolean add(KEY_TYPE o) { +#if TYPE_OBJECT + validate(o); +#endif if(tree == null) { tree = first = last = new EntryBRACES(o, null); size++; @@ -242,7 +245,7 @@ public class RB_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE } fixAfterInsertion(adding); size++; - return false; + return true; } @Override @@ -660,6 +663,7 @@ public class RB_TREE_SET KEY_GENERIC_TYPE extends ABSTRACT_SET KEY_GENERIC_TYPE } } + protected void validate(KEY_TYPE k) { compare(k, k); } protected int compare(KEY_TYPE k, KEY_TYPE v) { return comparator != null ? comparator.compare(k, v) : COMPAREABLE_TO_KEY(k, v);} protected static GENERIC_KEY_BRACES boolean isBlack(Entry KEY_GENERIC_TYPE p) { return p == null || p.isBlack(); } protected static GENERIC_KEY_BRACES Entry KEY_GENERIC_TYPE parentOf(Entry KEY_GENERIC_TYPE p) { return (p == null ? null : p.parent); } diff --git a/src/main/java/speiger/src/collections/utils/SanityChecks.java b/src/main/java/speiger/src/collections/utils/SanityChecks.java index 04285086..d510dabd 100644 --- a/src/main/java/speiger/src/collections/utils/SanityChecks.java +++ b/src/main/java/speiger/src/collections/utils/SanityChecks.java @@ -72,8 +72,8 @@ public class SanityChecks * @throws IllegalStateException if offset or accessSize is negative or the range goes out of the array-size */ public static void checkArrayCapacity(int arraySize, int offset, int accessSize) { - if(offset < 0) throw new IllegalStateException("Offset is negative ("+offset+")"); - else if(accessSize < 0) throw new IllegalStateException("Size is negative ("+accessSize+")"); + if(offset < 0) throw new IndexOutOfBoundsException("Offset is negative ("+offset+")"); + else if(accessSize < 0) throw new IllegalArgumentException("Size is negative ("+accessSize+")"); else if(arraySize < offset + accessSize) throw new IndexOutOfBoundsException("Index (" + (offset + accessSize) + ") is not in size (" + arraySize + ")"); } diff --git a/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java b/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java new file mode 100644 index 00000000..7769bdbd --- /dev/null +++ b/src/test/java/speiger/src/collections/objects/list/ObjectListTests.java @@ -0,0 +1,46 @@ +package speiger.src.collections.objects.list; + +import java.util.List; +import java.util.function.Function; + +import com.google.common.collect.testing.ListTestSuiteBuilder; +import com.google.common.collect.testing.TestStringListGenerator; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.ListFeature; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import speiger.src.collections.objects.lists.ImmutableObjectList; +import speiger.src.collections.objects.lists.ObjectArrayList; +import speiger.src.collections.objects.lists.ObjectLinkedList; + +@SuppressWarnings("javadoc") +public class ObjectListTests extends TestCase +{ + public static Test suite() + { + TestSuite suite = new TestSuite("Lists"); + suite.addTest(suite("ArrayList", T -> new ObjectArrayList<>(T))); + suite.addTest(suite("LinkedList", T -> new ObjectLinkedList<>(T))); + suite.addTest(immutableSuite("ImmutableList", T -> new ImmutableObjectList<>(T))); + return suite; + } + + public static Test suite(String name, Function> factory) + { + return ListTestSuiteBuilder.using(new TestStringListGenerator() { + @Override + protected List create(String[] elements) { return factory.apply(elements); } + }).named(name).withFeatures(ListFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_VALUES, CollectionSize.ANY).createTestSuite(); + } + + public static Test immutableSuite(String name, Function> factory) + { + return ListTestSuiteBuilder.using(new TestStringListGenerator() { + @Override + protected List create(String[] elements) { return factory.apply(elements); } + }).named(name).withFeatures(CollectionFeature.ALLOWS_NULL_VALUES, CollectionSize.ANY).createTestSuite(); + } +} diff --git a/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java b/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java index 62e9c698..6a23916f 100644 --- a/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java +++ b/src/test/java/speiger/src/collections/objects/map/ObjectMapTests.java @@ -6,7 +6,9 @@ import java.util.Objects; import java.util.function.BiFunction; import java.util.function.Supplier; +import com.google.common.collect.testing.AnEnum; import com.google.common.collect.testing.MapTestSuiteBuilder; +import com.google.common.collect.testing.TestEnumMapGenerator; import com.google.common.collect.testing.TestStringMapGenerator; import com.google.common.collect.testing.features.CollectionFeature; import com.google.common.collect.testing.features.CollectionSize; @@ -20,6 +22,9 @@ import speiger.src.collections.objects.maps.impl.customHash.Object2ObjectOpenCus import speiger.src.collections.objects.maps.impl.hash.Object2ObjectLinkedOpenHashMap; import speiger.src.collections.objects.maps.impl.hash.Object2ObjectOpenHashMap; import speiger.src.collections.objects.maps.impl.immutable.ImmutableObject2ObjectOpenHashMap; +import speiger.src.collections.objects.maps.impl.misc.Enum2ObjectMap; +import speiger.src.collections.objects.maps.impl.misc.LinkedEnum2ObjectMap; +import speiger.src.collections.objects.maps.impl.misc.Object2ObjectArrayMap; import speiger.src.collections.objects.maps.impl.tree.Object2ObjectAVLTreeMap; import speiger.src.collections.objects.maps.impl.tree.Object2ObjectRBTreeMap; import speiger.src.collections.objects.utils.ObjectStrategy; @@ -34,9 +39,14 @@ public class ObjectMapTests extends TestCase suite.addTest(suite("LinkedHashMap", Object2ObjectLinkedOpenHashMap::new, true)); suite.addTest(suite("CustomHashMap", () -> new Object2ObjectOpenCustomHashMap<>(Strategy.INSTANCE), true)); suite.addTest(suite("LinkedCustomHashMap", () -> new Object2ObjectLinkedOpenCustomHashMap<>(Strategy.INSTANCE), true)); - suite.addTest(suite("RBTreeMap", () -> new Object2ObjectRBTreeMap(Comparator.naturalOrder()), false)); - suite.addTest(suite("AVLTreeMap", () -> new Object2ObjectAVLTreeMap(Comparator.naturalOrder()), false)); + suite.addTest(suite("RBTreeMap_NonNull", Object2ObjectRBTreeMap::new, false)); + suite.addTest(suite("AVLTreeMap_NonNull", Object2ObjectAVLTreeMap::new, false)); + suite.addTest(suite("RBTreeMap_Null", () -> new Object2ObjectRBTreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())), true)); + suite.addTest(suite("AVLTreeMap_Null", () -> new Object2ObjectAVLTreeMap<>(Comparator.nullsFirst(Comparator.naturalOrder())), true)); suite.addTest(immutableSuit("ImmutableMap", ImmutableObject2ObjectOpenHashMap::new)); + suite.addTest(suite("ArrayMap", Object2ObjectArrayMap::new, true)); + suite.addTest(enumSuite("EnumMap", () -> new Enum2ObjectMap<>(AnEnum.class))); + suite.addTest(enumSuite("LinkedEnumMap", () -> new LinkedEnum2ObjectMap<>(AnEnum.class))); return suite; } @@ -72,6 +82,21 @@ public class ObjectMapTests extends TestCase return builder.createTestSuite(); } + public static Test enumSuite(String name, Supplier> factory) + { + MapTestSuiteBuilder builder = MapTestSuiteBuilder.using(new TestEnumMapGenerator() { + @Override + protected Map create(Map.Entry[] entries) { + Map map = factory.get(); + for(Map.Entry entry : entries) { + map.put(entry.getKey(), entry.getValue()); + } + return map; + } + }).named(name).withFeatures(CollectionSize.ANY, MapFeature.GENERAL_PURPOSE, MapFeature.ALLOWS_NULL_VALUES, CollectionFeature.SUPPORTS_ITERATOR_REMOVE); + return builder.createTestSuite(); + } + private static class Strategy implements ObjectStrategy { static final Strategy INSTANCE = new Strategy(); diff --git a/src/test/java/speiger/src/collections/objects/set/ObjectSetTests.java b/src/test/java/speiger/src/collections/objects/set/ObjectSetTests.java new file mode 100644 index 00000000..3c874c92 --- /dev/null +++ b/src/test/java/speiger/src/collections/objects/set/ObjectSetTests.java @@ -0,0 +1,80 @@ +package speiger.src.collections.objects.set; + +import java.util.Comparator; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; + +import com.google.common.collect.testing.SetTestSuiteBuilder; +import com.google.common.collect.testing.TestStringSetGenerator; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.SetFeature; + +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import speiger.src.collections.objects.sets.ImmutableObjectOpenHashSet; +import speiger.src.collections.objects.sets.ObjectAVLTreeSet; +import speiger.src.collections.objects.sets.ObjectArraySet; +import speiger.src.collections.objects.sets.ObjectLinkedOpenCustomHashSet; +import speiger.src.collections.objects.sets.ObjectLinkedOpenHashSet; +import speiger.src.collections.objects.sets.ObjectOpenCustomHashSet; +import speiger.src.collections.objects.sets.ObjectOpenHashSet; +import speiger.src.collections.objects.sets.ObjectRBTreeSet; +import speiger.src.collections.objects.utils.ObjectStrategy; + +@SuppressWarnings("javadoc") +public class ObjectSetTests extends TestCase +{ + public static Test suite() + { + TestSuite suite = new TestSuite("Sets"); + suite.addTest(suite("HashSet", ObjectOpenHashSet::new, true)); + suite.addTest(suite("LinkedHashSet", ObjectLinkedOpenHashSet::new, true)); + suite.addTest(suite("CustomHashSet", T -> new ObjectOpenCustomHashSet<>(T, Strategy.INSTANCE), true)); + suite.addTest(suite("LinkedCustomHashSet", T -> new ObjectLinkedOpenCustomHashSet<>(T, Strategy.INSTANCE), true)); + suite.addTest(immutableSuite("ImmutableHashSet", ImmutableObjectOpenHashSet::new)); + suite.addTest(suite("ArraySet", ObjectArraySet::new, true)); + suite.addTest(suite("RBTreeSet_NonNull", ObjectRBTreeSet::new, false)); + suite.addTest(suite("AVLTreeSet_NonNull", ObjectAVLTreeSet::new, false)); + suite.addTest(suite("RBTreeSet_Null", T -> new ObjectRBTreeSet<>(T, Comparator.nullsFirst(Comparator.naturalOrder())), true)); + suite.addTest(suite("AVLTreeSet_Null", T -> new ObjectAVLTreeSet<>(T, Comparator.nullsFirst(Comparator.naturalOrder())), true)); + return suite; + } + + public static Test suite(String name, Function> factory, boolean allowNull) + { + SetTestSuiteBuilder generator = SetTestSuiteBuilder.using(new TestStringSetGenerator() { + @Override + protected Set create(String[] elements) { return factory.apply(elements); } + }).named(name).withFeatures(CollectionSize.ANY, SetFeature.GENERAL_PURPOSE); + if(allowNull) generator.withFeatures(CollectionFeature.ALLOWS_NULL_VALUES); + return generator.createTestSuite(); + } + + + public static Test immutableSuite(String name, Function> factory) + { + return SetTestSuiteBuilder.using(new TestStringSetGenerator() { + @Override + protected Set create(String[] elements) { return factory.apply(elements); } + }).named(name).withFeatures(CollectionSize.ANY, CollectionFeature.ALLOWS_NULL_VALUES).createTestSuite(); + } + + private static class Strategy implements ObjectStrategy + { + static final Strategy INSTANCE = new Strategy(); + + @Override + public int hashCode(String o) { + return Objects.hashCode(o); + } + + @Override + public boolean equals(String key, String value) { + return Objects.equals(key, value); + } + + } +}