From 085a4382af40c9a80b05a9a7e953780e0acef53a Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 19 Dec 2023 01:41:05 +0800 Subject: [PATCH 01/28] Cache class field ref into local --- drv/ArrayFIFOQueue.drv | 3 ++- drv/ArrayList.drv | 44 ++++++++++++++++++++++---------------- drv/ArrayMap.drv | 37 +++++++++++++++++++++----------- drv/ArrayPriorityQueue.drv | 3 ++- drv/ArraySet.drv | 5 ++++- drv/HeapPriorityQueue.drv | 3 ++- drv/ImmutableList.drv | 20 +++++++++++------ drv/Iterators.drv | 1 + drv/OpenHashMap.drv | 18 +++++++++++++++- drv/OpenHashSet.drv | 5 +++-- drv/Spliterators.drv | 1 + drv/StripedOpenHashMap.drv | 1 + 12 files changed, 97 insertions(+), 44 deletions(-) diff --git a/drv/ArrayFIFOQueue.drv b/drv/ArrayFIFOQueue.drv index 5c3697da..79863da5 100644 --- a/drv/ArrayFIFOQueue.drv +++ b/drv/ArrayFIFOQueue.drv @@ -209,6 +209,7 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, s.defaultWriteObject(); int size = size(); s.writeInt(size); + final KEY_GENERIC_TYPE[] array = this.array; for(int i = start; size-- != 0;) { s.WRITE_KEY(array[i++]); if (i == length) i = 0; @@ -219,7 +220,7 @@ public class ARRAY_FIFO_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENERIC, private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); end = s.readInt(); - array = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[length = HashCommon.nextPowerOfTwo(end + 1)]; + final KEY_GENERIC_TYPE[] array = this.array = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[length = HashCommon.nextPowerOfTwo(end + 1)]; for(int i = 0; i < end; i++) array[i] = KEY_GENERIC_CAST s.READ_KEY(); } } diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index b529e420..9ecfca11 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -105,7 +105,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #endif /** The backing array. */ - protected transient KEY_GENERIC_TYPE a[]; + protected transient KEY_GENERIC_TYPE[] a; /** The current actual size of the list (never greater than the backing-array length). */ protected int size; @@ -137,7 +137,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @param a the array that will be used to back this array list. */ - protected ARRAY_LIST(final KEY_GENERIC_TYPE a[], @SuppressWarnings("unused") boolean wrapped) { + protected ARRAY_LIST(final KEY_GENERIC_TYPE[] a, @SuppressWarnings("unused") boolean wrapped) { this.a = a; #if ! KEYS_PRIMITIVE this.wrapped = wrapped; @@ -243,7 +243,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @param a an array whose elements will be used to fill the array list. */ - public ARRAY_LIST(final KEY_GENERIC_TYPE a[]) { + public ARRAY_LIST(final KEY_GENERIC_TYPE[] a) { this(a, 0, a.length); } @@ -254,7 +254,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @param length the number of elements to use. */ - public ARRAY_LIST(final KEY_GENERIC_TYPE a[], final int offset, final int length) { + public ARRAY_LIST(final KEY_GENERIC_TYPE[] a, final int offset, final int length) { this(length); System.arraycopy(a, offset, this.a, 0, length); size = length; @@ -320,7 +320,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @return a new array list of the given size, wrapping the given array. */ - public static KEY_GENERIC ARRAY_LIST KEY_GENERIC wrap(final KEY_GENERIC_TYPE a[], final int length) { + public static KEY_GENERIC ARRAY_LIST KEY_GENERIC wrap(final KEY_GENERIC_TYPE[] a, final int length) { if (length > a.length) throw new IllegalArgumentException("The specified length (" + length + ") is greater than the array size (" + a.length + ")"); final ARRAY_LIST KEY_GENERIC l = new ARRAY_LIST KEY_GENERIC_DIAMOND(a, true); l.size = length; @@ -337,7 +337,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @return a new array list wrapping the given array. */ - public static KEY_GENERIC ARRAY_LIST KEY_GENERIC wrap(final KEY_GENERIC_TYPE a[]) { + public static KEY_GENERIC ARRAY_LIST KEY_GENERIC wrap(final KEY_GENERIC_TYPE[] a) { return wrap(a, a.length); } @@ -451,7 +451,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements if (wrapped) a = ARRAYS.ensureCapacity(a, capacity, size); else { if (capacity > a.length) { - final Object t[] = new Object[capacity]; + final Object[] t = new Object[capacity]; System.arraycopy(a, 0, t, 0, size); a = (KEY_GENERIC_TYPE[])t; } @@ -476,7 +476,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #else if (wrapped) a = ARRAYS.forceCapacity(a, capacity, size); else { - final Object t[] = new Object[capacity]; + final Object[] t = new Object[capacity]; System.arraycopy(a, 0, t, 0, size); a = (KEY_GENERIC_TYPE[])t; } @@ -510,6 +510,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public int indexOf(final KEY_TYPE k) { + final KEY_TYPE[] a = this.a; for(int i = 0; i < size; i++) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -517,6 +518,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public int lastIndexOf(final KEY_TYPE k) { + final KEY_TYPE[] a = this.a; for(int i = size; i-- != 0;) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -524,6 +526,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public KEY_GENERIC_TYPE REMOVE_KEY(final int index) { if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")"); + final KEY_TYPE[] a = this.a; final KEY_GENERIC_TYPE old = a[index]; size--; if (index != size) System.arraycopy(a, index + 1, a, index, size - index); @@ -831,7 +834,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @param length the number of elements to add. */ @Override - public void addElements(final int index, final KEY_GENERIC_TYPE a[], final int offset, final int length) { + public void addElements(final int index, final KEY_GENERIC_TYPE[] a, final int offset, final int length) { ensureIndex(index); ARRAYS.ensureOffsetLength(a, offset, length); grow(size + length); @@ -848,7 +851,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * @param length the number of elements to add. */ @Override - public void setElements(final int index, final KEY_GENERIC_TYPE a[], final int offset, final int length) { + public void setElements(final int index, final KEY_GENERIC_TYPE[] a, final int offset, final int length) { ensureIndex(index); ARRAYS.ensureOffsetLength(a, offset, length); if (index + length > size) throw new IndexOutOfBoundsException("End index (" + (index + length) + ") is greater than list size (" + size + ")"); @@ -857,6 +860,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = this.a; for (int i = 0; i < size; ++i) { action.accept(a[i]); } @@ -1071,6 +1075,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = this.a; for (final int max = getWorkingMax(); pos < max; ++pos) { action.accept(a[pos]); } @@ -1229,7 +1234,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements SUPPRESS_WARNINGS_KEY_UNCHECKED public int compareTo(final ARRAY_LIST KEY_EXTENDS_GENERIC l) { final int s1 = size(), s2 = l.size(); - final KEY_GENERIC_TYPE a1[] = a, a2[] = l.a; + final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; #if KEYS_PRIMITIVE // Can't make this assumption for reference types in case we have a goofy Comparable that doesn't compare itself equal if (a1 == a2 && s1 == s2) return 0; #endif @@ -1263,13 +1268,14 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); + final KEY_GENERIC_TYPE[] a = this.a; for(int i = 0; i < size; i++) s.WRITE_KEY(a[i]); } SUPPRESS_WARNINGS_KEY_UNCHECKED private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - a = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[size]; + final KEY_GENERIC_TYPE[] a = this.a = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[size]; for(int i = 0; i < size; i++) a[i] = KEY_GENERIC_CAST s.READ_KEY(); } @@ -1321,9 +1327,9 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements int i; int j; - KEY_TYPE k[] = new KEY_TYPE[n]; - KEY_TYPE nk[] = new KEY_TYPE[n]; - int randIndexes[] = new int[n]; + KEY_TYPE[] k = new KEY_TYPE[n]; + KEY_TYPE[] nk = new KEY_TYPE[n]; + int[] randIndexes = new int[n]; long ns; for(i = 0; i < n; i++) { @@ -1514,8 +1520,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } private static Object[] k, v, nk; - private static KEY_TYPE kt[]; - private static KEY_TYPE nkt[]; + private static KEY_TYPE[] kt; + private static KEY_TYPE[] nkt; private static ARRAY_LIST topList; protected static void testLists(LIST m, java.util.List t, int n, int level) throws Exception { @@ -1786,7 +1792,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements int p = r.nextInt() % (2 * n + 1); Collection t1 = new java.util.ArrayList(); int s = r.nextInt(n / 2 + 1); - KEY_TYPE a[] = new KEY_TYPE [s]; + KEY_TYPE[] a = new KEY_TYPE [s]; for(int j = 0; j < s; j++) { KEY_TYPE x = genKey(); t1.add(KEY2OBJ(x)); @@ -2131,7 +2137,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } - public static void main(String args[]) throws Exception { + public static void main(String[] args) throws Exception { int n = Integer.parseInt(args[1]); if (args.length > 2) r = new java.util.Random(seed = Long.parseLong(args[2])); diff --git a/drv/ArrayMap.drv b/drv/ArrayMap.drv index 1d754947..4d84723b 100644 --- a/drv/ArrayMap.drv +++ b/drv/ArrayMap.drv @@ -177,7 +177,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { - // Hoist containing class field ref into local + final KEY_TYPE[] key = this.key; + final VALUE_TYPE[] value = this.value; final int max = size; while (next < max) { action.accept(new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[curr = next], VALUE_GENERIC_CAST value[next++])); @@ -222,7 +223,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { - // Hoist containing class field ref into local + final KEY_TYPE[] key = this.key; + final VALUE_TYPE[] value = this.value; final int max = size; while (next < max) { entry.key = KEY_GENERIC_CAST key[curr = next]; @@ -268,7 +270,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEach(final Consumer action) { - // Hoist containing class field ref into local + final KEY_TYPE[] key = this.key; + final VALUE_TYPE[] value = this.value; for (int i = 0, max = size; i < max; ++i) { action.accept(new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[i], VALUE_GENERIC_CAST value[i])); } @@ -278,8 +281,9 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void fastForEach(final Consumer action) { + final KEY_TYPE[] key = this.key; + final VALUE_TYPE[] value = this.value; final BasicEntry KEY_VALUE_GENERIC entry = new BasicEntry KEY_VALUE_GENERIC_DIAMOND (); - // Hoist containing class field ref into local for (int i = 0, max = size; i < max; ++i) { entry.key = KEY_GENERIC_CAST key[i]; entry.value = VALUE_GENERIC_CAST value[i]; @@ -360,6 +364,12 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override public void clear() { +#if KEYS_REFERENCE + final KEY_TYPE[] key = this.key; +#endif +#if VALUES_REFERENCE + final VALUE_TYPE[] value = this.value; +#endif #if KEYS_REFERENCE || VALUES_REFERENCE for(int i = size; i-- != 0;) { #if KEYS_REFERENCE @@ -378,6 +388,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override public boolean containsValue(VALUE_TYPE v) { + final VALUE_TYPE[] value = this.value; for(int i = size; i-- != 0;) if (VALUE_EQUALS(value[i], v)) return true; return false; } @@ -487,7 +498,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - // Hoist containing class field ref into local + final KEY_TYPE[] key = this.key; final int max = size; while (pos < max) { action.accept(KEY_GENERIC_CAST key[pos++]); @@ -522,7 +533,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - // Hoist containing class field ref into local + final KEY_TYPE[] key = this.key; final int max = size; while (pos < max) { action.accept(KEY_GENERIC_CAST key[pos++]); @@ -538,7 +549,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public void forEach(METHOD_ARG_KEY_CONSUMER action) { - // Hoist containing class field ref into local + final KEY_TYPE[] key = this.key; for (int i = 0, max = size; i < max; ++i) { action.accept(KEY_GENERIC_CAST key[i]); } @@ -602,7 +613,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_VALUE_UNCHECKED public void forEachRemaining(final METHOD_ARG_VALUE_CONSUMER action) { - // Hoist containing class field ref into local + final VALUE_TYPE[] value = this.value; final int max = size; while (pos < max) { action.accept(VALUE_GENERIC_CAST value[pos++]); @@ -638,7 +649,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_VALUE_UNCHECKED public void forEachRemaining(final METHOD_ARG_VALUE_CONSUMER action) { - // Hoist containing class field ref into local + final VALUE_TYPE[] value = this.value; final int max = size; while (pos < max) { action.accept(VALUE_GENERIC_CAST value[pos++]); @@ -654,7 +665,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_VALUE_UNCHECKED public void forEach(METHOD_ARG_VALUE_CONSUMER action) { - // Hoist containing class field ref into local + final VALUE_TYPE[] value = this.value; for (int i = 0, max = size; i < max; ++i) { action.accept(VALUE_GENERIC_CAST value[i]); } @@ -704,6 +715,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); + final KEY_TYPE[] key = this.key; + final VALUE_TYPE[] value = this.value; for(int i = 0, max = size; i < max; i++) { s.WRITE_KEY(key[i]); s.WRITE_VALUE(value[i]); @@ -712,8 +725,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - key = new KEY_TYPE[size]; - value = new VALUE_TYPE[size]; + final KEY_TYPE[] key = this.key = new KEY_TYPE[size]; + final VALUE_TYPE[] value = this.value = new VALUE_TYPE[size]; for(int i = 0; i < size; i++) { key[i] = s.READ_KEY(); value[i] = s.READ_VALUE(); diff --git a/drv/ArrayPriorityQueue.drv b/drv/ArrayPriorityQueue.drv index f3465a2d..7445406d 100644 --- a/drv/ArrayPriorityQueue.drv +++ b/drv/ArrayPriorityQueue.drv @@ -219,13 +219,14 @@ public class ARRAY_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENE private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(array.length); + final KEY_GENERIC_TYPE[] array = this.array; for(int i = 0; i < size; i++) s.WRITE_KEY(array[i]); } SUPPRESS_WARNINGS_KEY_UNCHECKED private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - array = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[s.readInt()]; + final KEY_GENERIC_TYPE[] array = this.array = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[s.readInt()]; for(int i = 0; i < size; i++) array[i] = KEY_GENERIC_CAST s.READ_KEY(); } } diff --git a/drv/ArraySet.drv b/drv/ArraySet.drv index 27bb2ead..b872a04e 100644 --- a/drv/ArraySet.drv +++ b/drv/ArraySet.drv @@ -183,6 +183,7 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j } private int findKey(final KEY_TYPE o) { + final KEY_TYPE[] a = this.a; for(int i = size; i-- != 0;) if (KEY_EQUALS(a[i], o)) return i; return -1; } @@ -274,6 +275,7 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_TYPE[] a = this.a; for (final int max = getWorkingMax(); pos < max; ++pos) { action.accept(KEY_GENERIC_CAST a[pos]); } @@ -431,12 +433,13 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); + final KEY_TYPE[] a = this.a; for(int i = 0; i < size; i++) s.WRITE_KEY(a[i]); } private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - a = new KEY_TYPE[size]; + final KEY_TYPE[] a = this.a = new KEY_TYPE[size]; for(int i = 0; i < size; i++) a[i] = s.READ_KEY(); } } diff --git a/drv/HeapPriorityQueue.drv b/drv/HeapPriorityQueue.drv index 549349d4..8cf0e27d 100644 --- a/drv/HeapPriorityQueue.drv +++ b/drv/HeapPriorityQueue.drv @@ -267,13 +267,14 @@ public class HEAP_PRIORITY_QUEUE KEY_GENERIC implements PRIORITY_QUEUE KEY_GENER private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); s.writeInt(heap.length); + final KEY_GENERIC_TYPE[] heap = this.heap; for(int i = 0; i < size; i++) s.WRITE_KEY(heap[i]); } SUPPRESS_WARNINGS_KEY_UNCHECKED private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - heap = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[s.readInt()]; + final KEY_GENERIC_TYPE[] heap = this.heap = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[s.readInt()]; for(int i = 0; i < size; i++) heap[i] = KEY_GENERIC_CAST s.READ_KEY(); } diff --git a/drv/ImmutableList.drv b/drv/ImmutableList.drv index 20261015..e7bccc2c 100644 --- a/drv/ImmutableList.drv +++ b/drv/ImmutableList.drv @@ -56,7 +56,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE #endif /** The backing array; all elements are part of this list. */ - private final KEY_GENERIC_TYPE a[]; + private final KEY_GENERIC_TYPE[] a; /** Creates a new immutable list using a given array. * @@ -65,7 +65,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE * @param a the array that will be used to back this immutable list. */ - public IMMUTABLE_LIST(final KEY_GENERIC_TYPE a[]) { + public IMMUTABLE_LIST(final KEY_GENERIC_TYPE[] a) { this.a = a; } @@ -107,7 +107,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE * @param length the number of elements to use. */ SUPPRESS_WARNINGS_KEY_UNCHECKED - public IMMUTABLE_LIST(final KEY_GENERIC_TYPE a[], final int offset, final int length) { + public IMMUTABLE_LIST(final KEY_GENERIC_TYPE[] a, final int offset, final int length) { this(length == 0 ? _EMPTY_ARRAY : KEY_GENERIC_ARRAY_CAST new KEY_TYPE[length]); System.arraycopy(a, offset, this.a, 0, length); } @@ -146,7 +146,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE if (arrayList.isEmpty()) { return of(); } - KEY_GENERIC_TYPE backingArray[] = arrayList.elements(); + KEY_GENERIC_TYPE[] backingArray = arrayList.elements(); if (arrayList.size() != backingArray.length) { backingArray = java.util.Arrays.copyOf(backingArray, arrayList.size()); } @@ -220,6 +220,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public int indexOf(final KEY_TYPE k) { + final KEY_TYPE[] a = this.a; for(int i = 0, size = a.length; i < size; i++) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -227,6 +228,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public int lastIndexOf(final KEY_TYPE k) { + final KEY_TYPE[] a = this.a; for(int i = a.length; i-- != 0;) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -256,6 +258,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = this.a; for (int i = 0; i < a.length; ++i) { action.accept(a[i]); } @@ -397,6 +400,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = this.a; for (; pos < max; ++pos) { action.accept(a[pos]); } @@ -443,7 +447,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE * the first element of the {@code innerList}, not this sublist. {@code a[from]} is the * first element of this sublist. */ - final transient KEY_GENERIC_TYPE a[]; + final transient KEY_GENERIC_TYPE[] a; /** No validation; callers must validate arguments. */ ImmutableSubList(IMMUTABLE_LIST KEY_GENERIC innerList, int from, int to) { @@ -461,12 +465,14 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public int indexOf(final KEY_TYPE k) { + final KEY_TYPE[] a = this.a; for(int i = from; i < to; i++) if (KEY_EQUALS(k, a[i])) return i - from; return -1; } @Override public int lastIndexOf(final KEY_TYPE k) { + final KEY_TYPE[] a = this.a; for(int i = to; i-- != from;) if (KEY_EQUALS(k, a[i])) return i - from; return -1; } @@ -492,6 +498,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = this.a; for (int i = from; i < to; ++i) { action.accept(a[i]); } @@ -678,6 +685,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE #if ! KEYS_USE_REFERENCE_EQUALITY SUPPRESS_WARNINGS_KEY_UNCHECKED int contentsCompareTo(KEY_GENERIC_TYPE[] otherA, int otherAFrom, int otherATo) { + final KEY_GENERIC_TYPE[] a = this.a; #if KEYS_PRIMITIVE // Can't make this assumption for reference types in case we have a goofy Comparable that doesn't compare itself equal if (a == otherA && from == otherAFrom && to == otherATo) return 0; #endif @@ -821,7 +829,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE #endif // TODO When minimum version of Java becomes Java 9, use Arrays.compare, which vectorizes. final int s1 = size(), s2 = l.size(); - final KEY_GENERIC_TYPE a1[] = a, a2[] = l.a; + final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; KEY_GENERIC_TYPE e1, e2; int r, i; diff --git a/drv/Iterators.drv b/drv/Iterators.drv index 56d3392e..42a96e60 100644 --- a/drv/Iterators.drv +++ b/drv/Iterators.drv @@ -242,6 +242,7 @@ public final class ITERATORS { public void forEachRemaining(final Consumer action) { #endif Objects.requireNonNull(action); + final KEY_GENERIC_TYPE[] array = this.array; for (; curr < length; ++curr) { action.accept(array[offset + curr]); } diff --git a/drv/OpenHashMap.drv b/drv/OpenHashMap.drv index b06c4acf..a276d704 100644 --- a/drv/OpenHashMap.drv +++ b/drv/OpenHashMap.drv @@ -693,6 +693,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE int last, slot; KEY_GENERIC_TYPE curr; final KEY_GENERIC_TYPE[] key = this.key; + final VALUE_GENERIC_TYPE value[] = this.value; for(;;) { pos = ((last = pos) + 1) & mask; @@ -1077,8 +1078,8 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE @Override public boolean containsValue(final VALUE_TYPE v) { - final VALUE_GENERIC_TYPE value[] = this.value; final KEY_GENERIC_TYPE key[] = this.key; + final VALUE_GENERIC_TYPE value[] = this.value; if (containsNullKey && VALUE_EQUALS(value[n], v)) return true; for(int i = n; i-- != 0;) if (! KEY_IS_NULL(key[i]) && VALUE_EQUALS(value[i], v)) return true; return false; @@ -1682,6 +1683,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE else { KEY_GENERIC_TYPE curr; final KEY_GENERIC_TYPE[] key = OPEN_HASH_MAP.this.key; + final VALUE_GENERIC_TYPE value[] = OPEN_HASH_MAP.this.value; // We have to horribly duplicate the shiftKeys() code because we need to update next/prev. for(;;) { pos = ((last = pos) + 1) & mask; @@ -1876,6 +1878,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE int last, slot; KEY_GENERIC_TYPE curr; final KEY_GENERIC_TYPE[] key = OPEN_HASH_MAP.this.key; + final VALUE_GENERIC_TYPE value[] = OPEN_HASH_MAP.this.value; for(;;) { pos = ((last = pos) + 1) & mask; @@ -2306,6 +2309,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final Consumer consumer) { + final long[] link = this.link; for(int i = size, curr, next = first; i-- != 0;) { curr = next; next = GET_NEXT(link[curr]); @@ -2317,6 +2321,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE @Override public void fastForEach(final Consumer consumer) { final MapEntry entry = new MapEntry(); + final long[] link = this.link; for(int i = size, next = first; i-- != 0;) { entry.index = next; next = GET_NEXT(link[next]); @@ -2330,6 +2335,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE @Override public void forEach(final Consumer consumer) { if (containsNullKey) consumer.accept(new MapEntry(n)); + final KEY_GENERIC_TYPE key[] = OPEN_HASH_MAP.this.key; for(int pos = n; pos-- != 0;) if (! KEY_IS_NULL(key[pos])) consumer.accept(new MapEntry(pos)); } @@ -2342,6 +2348,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE entry.index = n; consumer.accept(entry); } + final KEY_GENERIC_TYPE key[] = OPEN_HASH_MAP.this.key; for(int pos = n; pos-- != 0;) if (! KEY_IS_NULL(key[pos])) { entry.index = pos; @@ -2447,6 +2454,8 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final METHOD_ARG_KEY_CONSUMER consumer) { + final long[] link = OPEN_HASH_MAP.this.link; + final KEY_GENERIC_TYPE key[] = OPEN_HASH_MAP.this.key; for(int i = size, curr, next = first; i-- != 0;) { curr = next; next = GET_NEXT(link[curr]); @@ -2466,6 +2475,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final METHOD_ARG_KEY_CONSUMER consumer) { + final KEY_GENERIC_TYPE key[] = OPEN_HASH_MAP.this.key; if (containsNullKey) consumer.accept(key[n]); for(int pos = n; pos-- != 0;) { final KEY_GENERIC_TYPE k = key[pos]; @@ -2608,6 +2618,8 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final METHOD_ARG_VALUE_CONSUMER consumer) { + final long[] link = OPEN_HASH_MAP.this.link; + final VALUE_GENERIC_TYPE value[] = OPEN_HASH_MAP.this.value; for(int i = size, curr, next = first; i-- != 0;) { curr = next; next = GET_NEXT(link[curr]); @@ -2621,6 +2633,8 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final METHOD_ARG_VALUE_CONSUMER consumer) { + final KEY_GENERIC_TYPE key[] = OPEN_HASH_MAP.this.key; + final VALUE_GENERIC_TYPE value[] = OPEN_HASH_MAP.this.value; if (containsNullKey) consumer.accept(value[n]); for(int pos = n; pos-- != 0;) if (! KEY_IS_NULL(key[pos])) consumer.accept(value[pos]); @@ -2814,6 +2828,8 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE @Override public int hashCode() { int h = 0; + final KEY_GENERIC_TYPE key[] = this.key; + final VALUE_GENERIC_TYPE value[] = this.value; for(int j = realSize(), i = 0, t = 0; j-- != 0;) { while(KEY_IS_NULL(key[i])) i++; #if KEYS_REFERENCE diff --git a/drv/OpenHashSet.drv b/drv/OpenHashSet.drv index 624d2449..9b8a705f 100644 --- a/drv/OpenHashSet.drv +++ b/drv/OpenHashSet.drv @@ -1611,12 +1611,12 @@ public class OPEN_HASH_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implemen public KEY_GENERIC_TYPE NEXT_KEY() { if (! hasNext()) throw new NoSuchElementException(); c--; + final KEY_GENERIC_TYPE key[] = OPEN_HASH_SET.this.key; if (mustReturnNull) { mustReturnNull = false; last = n; return key[n]; } - final KEY_GENERIC_TYPE key[] = OPEN_HASH_SET.this.key; for(;;) { if (--pos < 0) { // We are just enumerating elements from the wrapped list. @@ -1842,8 +1842,8 @@ public class OPEN_HASH_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implemen @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { - if (containsNull) action.accept(key[n]); final KEY_GENERIC_TYPE key[] = this.key; + if (containsNull) action.accept(key[n]); for(int pos = n; pos-- != 0; ) if (! KEY_IS_NULL(key[pos])) action.accept(key[pos]); } @@ -2008,6 +2008,7 @@ public class OPEN_HASH_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implemen @Override public int hashCode() { int h = 0; + final KEY_GENERIC_TYPE[] key = OPEN_HASH_SET.this.key; for(int j = realSize(), i = 0; j-- != 0;) { while(KEY_IS_NULL(key[i])) i++; #if KEYS_REFERENCE diff --git a/drv/Spliterators.drv b/drv/Spliterators.drv index 757a3915..cce324d6 100644 --- a/drv/Spliterators.drv +++ b/drv/Spliterators.drv @@ -268,6 +268,7 @@ public final class SPLITERATORS { @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { Objects.requireNonNull(action); + final KEY_GENERIC_TYPE[] array = this.array; for (; curr < length; ++curr) { action.accept(array[offset + curr]); } diff --git a/drv/StripedOpenHashMap.drv b/drv/StripedOpenHashMap.drv index 3c2d7a61..93d63fed 100644 --- a/drv/StripedOpenHashMap.drv +++ b/drv/StripedOpenHashMap.drv @@ -150,6 +150,7 @@ public class STRIPED_OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VA #endif public int size() { + final ReentrantReadWriteLock[] lock = this.lock; int size = 0; for(int stripe = lock.length; stripe-- != 0;) { final ReadLock readLock = lock[stripe].readLock(); From a4097b3b7bec63fc6a5110ee0a5ae375b90c3feb Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 19 Dec 2023 12:27:54 +0800 Subject: [PATCH 02/28] Fix some wrong and missing --- drv/ArrayList.drv | 7 +++++-- drv/ArrayMap.drv | 28 ++++++++++++++-------------- drv/ArraySet.drv | 2 +- drv/ImmutableList.drv | 5 ++++- 4 files changed, 24 insertions(+), 18 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index 9ecfca11..4042e69f 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -526,7 +526,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public KEY_GENERIC_TYPE REMOVE_KEY(final int index) { if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")"); - final KEY_TYPE[] a = this.a; + final KEY_GENERIC_TYPE[] a = this.a; final KEY_GENERIC_TYPE old = a[index]; size--; if (index != size) System.arraycopy(a, index + 1, a, index, size - index); @@ -661,6 +661,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; final int max = to - from; while(pos < max) { action.accept(a[from + (lastReturned = pos++)]); @@ -701,6 +702,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; final int max = getMaxPos(); while(pos < max) { action.accept(a[pos++]); @@ -998,6 +1000,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; while (pos < size) { action.accept(a[last = pos++]); } @@ -1075,7 +1078,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_GENERIC_TYPE[] a = this.a; + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; for (final int max = getWorkingMax(); pos < max; ++pos) { action.accept(a[pos]); } diff --git a/drv/ArrayMap.drv b/drv/ArrayMap.drv index 4d84723b..5f13d907 100644 --- a/drv/ArrayMap.drv +++ b/drv/ArrayMap.drv @@ -177,8 +177,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { - final KEY_TYPE[] key = this.key; - final VALUE_TYPE[] value = this.value; + final KEY_TYPE[] key = ARRAY_MAP.this.key; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; final int max = size; while (next < max) { action.accept(new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[curr = next], VALUE_GENERIC_CAST value[next++])); @@ -223,8 +223,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { - final KEY_TYPE[] key = this.key; - final VALUE_TYPE[] value = this.value; + final KEY_TYPE[] key = ARRAY_MAP.this.key; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; final int max = size; while (next < max) { entry.key = KEY_GENERIC_CAST key[curr = next]; @@ -270,8 +270,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEach(final Consumer action) { - final KEY_TYPE[] key = this.key; - final VALUE_TYPE[] value = this.value; + final KEY_TYPE[] key = ARRAY_MAP.this.key; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; for (int i = 0, max = size; i < max; ++i) { action.accept(new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[i], VALUE_GENERIC_CAST value[i])); } @@ -281,8 +281,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void fastForEach(final Consumer action) { - final KEY_TYPE[] key = this.key; - final VALUE_TYPE[] value = this.value; + final KEY_TYPE[] key = ARRAY_MAP.this.key; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; final BasicEntry KEY_VALUE_GENERIC entry = new BasicEntry KEY_VALUE_GENERIC_DIAMOND (); for (int i = 0, max = size; i < max; ++i) { entry.key = KEY_GENERIC_CAST key[i]; @@ -498,7 +498,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_TYPE[] key = this.key; + final KEY_TYPE[] key = ARRAY_MAP.this.key; final int max = size; while (pos < max) { action.accept(KEY_GENERIC_CAST key[pos++]); @@ -533,7 +533,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_TYPE[] key = this.key; + final KEY_TYPE[] key = ARRAY_MAP.this.key; final int max = size; while (pos < max) { action.accept(KEY_GENERIC_CAST key[pos++]); @@ -549,7 +549,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public void forEach(METHOD_ARG_KEY_CONSUMER action) { - final KEY_TYPE[] key = this.key; + final KEY_TYPE[] key = ARRAY_MAP.this.key; for (int i = 0, max = size; i < max; ++i) { action.accept(KEY_GENERIC_CAST key[i]); } @@ -613,7 +613,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_VALUE_UNCHECKED public void forEachRemaining(final METHOD_ARG_VALUE_CONSUMER action) { - final VALUE_TYPE[] value = this.value; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; final int max = size; while (pos < max) { action.accept(VALUE_GENERIC_CAST value[pos++]); @@ -649,7 +649,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_VALUE_UNCHECKED public void forEachRemaining(final METHOD_ARG_VALUE_CONSUMER action) { - final VALUE_TYPE[] value = this.value; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; final int max = size; while (pos < max) { action.accept(VALUE_GENERIC_CAST value[pos++]); @@ -665,7 +665,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_VALUE_UNCHECKED public void forEach(METHOD_ARG_VALUE_CONSUMER action) { - final VALUE_TYPE[] value = this.value; + final VALUE_TYPE[] value = ARRAY_MAP.this.value; for (int i = 0, max = size; i < max; ++i) { action.accept(VALUE_GENERIC_CAST value[i]); } diff --git a/drv/ArraySet.drv b/drv/ArraySet.drv index b872a04e..a5b6544e 100644 --- a/drv/ArraySet.drv +++ b/drv/ArraySet.drv @@ -275,7 +275,7 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_TYPE[] a = this.a; + final KEY_TYPE[] a = ARRAY_SET.this.a; for (final int max = getWorkingMax(); pos < max; ++pos) { action.accept(KEY_GENERIC_CAST a[pos]); } diff --git a/drv/ImmutableList.drv b/drv/ImmutableList.drv index e7bccc2c..a8ef3ad8 100644 --- a/drv/ImmutableList.drv +++ b/drv/ImmutableList.drv @@ -323,6 +323,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE public int previousIndex() { return pos - 1; } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = IMMUTABLE_LIST.this.a; while (pos < a.length) { action.accept(a[pos++]); } @@ -400,7 +401,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_GENERIC_TYPE[] a = this.a; + final KEY_GENERIC_TYPE[] a = IMMUTABLE_LIST.this.a; for (; pos < max; ++pos) { action.accept(a[pos]); } @@ -561,6 +562,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE public int previousIndex() { return pos - from - 1; } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = IMMUTABLE_LIST.this.a; while (pos < to) { action.accept(a[pos++]); } @@ -631,6 +633,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = IMMUTABLE_LIST.this.a; final int max = maxPos; while(pos < max) { action.accept(a[pos++]); From ad17ac4e96b8edef9e46c87ff802d1ab919f6967 Mon Sep 17 00:00:00 2001 From: Mouse Date: Wed, 20 Dec 2023 15:46:07 +0800 Subject: [PATCH 03/28] Fix some wrong --- drv/OpenHashMap.drv | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drv/OpenHashMap.drv b/drv/OpenHashMap.drv index a276d704..d0c00d00 100644 --- a/drv/OpenHashMap.drv +++ b/drv/OpenHashMap.drv @@ -2309,7 +2309,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final Consumer consumer) { - final long[] link = this.link; + final long[] link = OPEN_HASH_MAP.this.link; for(int i = size, curr, next = first; i-- != 0;) { curr = next; next = GET_NEXT(link[curr]); @@ -2321,7 +2321,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE @Override public void fastForEach(final Consumer consumer) { final MapEntry entry = new MapEntry(); - final long[] link = this.link; + final long[] link = OPEN_HASH_MAP.this.link; for(int i = size, next = first; i-- != 0;) { entry.index = next; next = GET_NEXT(link[next]); From c64b26d1d3a26dc7c7430c495f766d328ff6a738 Mon Sep 17 00:00:00 2001 From: Mouse Date: Wed, 20 Dec 2023 16:02:53 +0800 Subject: [PATCH 04/28] Fix some wrong --- drv/ImmutableList.drv | 4 ++-- drv/OpenHashMap.drv | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drv/ImmutableList.drv b/drv/ImmutableList.drv index a8ef3ad8..4e7fd091 100644 --- a/drv/ImmutableList.drv +++ b/drv/ImmutableList.drv @@ -562,7 +562,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE public int previousIndex() { return pos - from - 1; } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_GENERIC_TYPE[] a = IMMUTABLE_LIST.this.a; + final KEY_GENERIC_TYPE[] a = ImmutableSubList.this.a; while (pos < to) { action.accept(a[pos++]); } @@ -633,7 +633,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE } @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_GENERIC_TYPE[] a = IMMUTABLE_LIST.this.a; + final KEY_GENERIC_TYPE[] a = ImmutableSubList.this.a; final int max = maxPos; while(pos < max) { action.accept(a[pos++]); diff --git a/drv/OpenHashMap.drv b/drv/OpenHashMap.drv index d0c00d00..e4f95385 100644 --- a/drv/OpenHashMap.drv +++ b/drv/OpenHashMap.drv @@ -2309,7 +2309,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE /** {@inheritDoc} */ @Override public void forEach(final Consumer consumer) { - final long[] link = OPEN_HASH_MAP.this.link; + final long[] link = OPEN_HASH_MAP.this.link; for(int i = size, curr, next = first; i-- != 0;) { curr = next; next = GET_NEXT(link[curr]); @@ -2321,7 +2321,7 @@ public class OPEN_HASH_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENE @Override public void fastForEach(final Consumer consumer) { final MapEntry entry = new MapEntry(); - final long[] link = OPEN_HASH_MAP.this.link; + final long[] link = OPEN_HASH_MAP.this.link; for(int i = size, next = first; i-- != 0;) { entry.index = next; next = GET_NEXT(link[next]); From 32b68b982bc9b49b5198760f42be6cf88e17a77b Mon Sep 17 00:00:00 2001 From: Barak Ugav Date: Wed, 7 Feb 2024 09:22:53 +0200 Subject: [PATCH 05/28] Immutable sub list iterator bug fix, ensureIndex is relative to sub range --- drv/ImmutableList.drv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drv/ImmutableList.drv b/drv/ImmutableList.drv index 43fddfa3..24ec70f4 100644 --- a/drv/ImmutableList.drv +++ b/drv/ImmutableList.drv @@ -543,7 +543,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public KEY_LIST_ITERATOR KEY_GENERIC listIterator(final int index) { - ensureRestrictedIndex(index + from); + ensureRestrictedIndex(index); return new KEY_LIST_ITERATOR KEY_GENERIC() { int pos = index + from; From 0f5313322a701e20a4268ec1bf6406addcf696cb Mon Sep 17 00:00:00 2001 From: Barak Ugav Date: Wed, 7 Feb 2024 10:47:54 +0200 Subject: [PATCH 06/28] Immutable sub list iterator bug fix, listIterator(size()) should be ok --- drv/ImmutableList.drv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drv/ImmutableList.drv b/drv/ImmutableList.drv index 24ec70f4..dfc45246 100644 --- a/drv/ImmutableList.drv +++ b/drv/ImmutableList.drv @@ -543,7 +543,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public KEY_LIST_ITERATOR KEY_GENERIC listIterator(final int index) { - ensureRestrictedIndex(index); + ensureIndex(index); return new KEY_LIST_ITERATOR KEY_GENERIC() { int pos = index + from; From ed0c36222015f598e0874f39767c11705811b94d Mon Sep 17 00:00:00 2001 From: Mouse Date: Mon, 12 Feb 2024 23:08:51 +0800 Subject: [PATCH 07/28] Optimize ArrayList --- drv/ArrayList.drv | 155 +++++++++++++++++++++++++++------------------- 1 file changed, 91 insertions(+), 64 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index b2333ef9..dab05190 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -117,7 +117,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * with {@link #wrap}. */ SUPPRESS_WARNINGS_KEY_UNCHECKED - private static final KEY_GENERIC KEY_GENERIC_TYPE[] copyArraySafe(KEY_GENERIC_TYPE[] a, int length) { + private static KEY_GENERIC KEY_GENERIC_TYPE[] copyArraySafe(KEY_GENERIC_TYPE[] a, int length) { if (length == 0) return KEY_GENERIC_ARRAY_CAST ARRAYS.EMPTY_ARRAY; #if KEYS_PRIMITIVE return java.util.Arrays.copyOf(a, length); @@ -126,7 +126,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #endif } - private static final KEY_GENERIC KEY_GENERIC_TYPE[] copyArrayFromSafe(ARRAY_LIST KEY_GENERIC l) { + private static KEY_GENERIC KEY_GENERIC_TYPE[] copyArrayFromSafe(ARRAY_LIST KEY_GENERIC l) { return copyArraySafe(l.a, l.size); } @@ -450,11 +450,9 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #else if (wrapped) a = ARRAYS.ensureCapacity(a, capacity, size); else { - if (capacity > a.length) { - final Object[] t = new Object[capacity]; - System.arraycopy(a, 0, t, 0, size); - a = (KEY_GENERIC_TYPE[])t; - } + final Object[] t = new Object[capacity]; + System.arraycopy(a, 0, t, 0, size); + a = (KEY_GENERIC_TYPE[])t; } #endif assert size <= a.length; @@ -488,6 +486,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public void add(final int index, final KEY_GENERIC_TYPE k) { ensureIndex(index); grow(size + 1); + final KEY_GENERIC_TYPE[] a = this.a; if (index != size) System.arraycopy(a, index, a, index + 1, size - index); a[index] = k; size++; @@ -511,7 +510,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public int indexOf(final KEY_TYPE k) { final KEY_TYPE[] a = this.a; - for(int i = 0; i < size; i++) if (KEY_EQUALS(k, a[i])) return i; + for(int i = 0, max = size; i < max; i++) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -528,12 +527,11 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")"); final KEY_GENERIC_TYPE[] a = this.a; final KEY_GENERIC_TYPE old = a[index]; - size--; + final int size = --this.size; if (index != size) System.arraycopy(a, index + 1, a, index, size - index); #if KEYS_REFERENCE a[size] = null; #endif - assert size <= a.length; return old; } @@ -542,14 +540,14 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements int index = indexOf(k); if (index == -1) return false; REMOVE_KEY(index); - assert size <= a.length; return true; } @Override public KEY_GENERIC_TYPE set(final int index, final KEY_GENERIC_TYPE k) { if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")"); - KEY_GENERIC_TYPE old = a[index]; + final KEY_GENERIC_TYPE[] a = this.a; + final KEY_GENERIC_TYPE old = a[index]; a[index] = k; return old; } @@ -560,7 +558,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements Arrays.fill(a, 0, size, null); #endif size = 0; - assert size <= a.length; } @Override @@ -570,10 +567,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void size(final int size) { - if (size > a.length) a = ARRAYS.forceCapacity(a, size, this.size); - if (size > this.size) Arrays.fill(a, this.size, size, KEY_NULL); + KEY_GENERIC_TYPE[] a = this.a; + final int oldSize = this.size; + if (size > a.length) a = this.a = ARRAYS.forceCapacity(a, size, oldSize); + if (size > oldSize) Arrays.fill(a, oldSize, size, KEY_NULL); #if KEYS_REFERENCE - else Arrays.fill(a, size, this.size, KEY_NULL); + else Arrays.fill(a, size, oldSize, KEY_NULL); #endif this.size = size; } @@ -609,11 +608,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements SUPPRESS_WARNINGS_KEY_UNCHECKED public void trim(final int n) { // TODO: use Arrays.trim() and preserve type only if necessary + final KEY_GENERIC_TYPE[] a = this.a; + final int size = this.size; if (n >= a.length || size == a.length) return; - final KEY_GENERIC_TYPE t[] = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[Math.max(n, size)]; + final KEY_GENERIC_TYPE[] t = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[Math.max(n, size)]; System.arraycopy(a, 0, t, 0, size); - a = t; - assert size <= a.length; + this.a = t; } private class SubList extends ABSTRACT_LIST.SUBLIST_RANDOM_ACCESS KEY_GENERIC { @@ -636,6 +636,14 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements return a[i + from]; } + @Override + public void forEach(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; + for (int i = from, max = to; i < max; ++i) { + action.accept(a[i]); + } + } + private final class SubListIterator extends ITERATORS.AbstractIndexBasedListIterator KEY_GENERIC { // We are using pos == 0 to be 0 relative to SubList.from (meaning you need to do a[from + i] when accessing array). @@ -644,15 +652,15 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } @Override - protected final KEY_GENERIC_TYPE get(int i) { return a[from + i]; } + protected KEY_GENERIC_TYPE get(int i) { return a[from + i]; } @Override - protected final void add(int i, KEY_GENERIC_TYPE k) { SubList.this.add(i, k); } + protected void add(int i, KEY_GENERIC_TYPE k) { SubList.this.add(i, k); } @Override - protected final void set(int i, KEY_GENERIC_TYPE k) { SubList.this.set(i, k); } + protected void set(int i, KEY_GENERIC_TYPE k) { SubList.this.set(i, k); } @Override - protected final void remove(int i) { SubList.this.REMOVE_KEY(i); } + protected void remove(int i) { SubList.this.REMOVE_KEY(i); } @Override - protected final int getMaxPos() { return to - from; } + protected int getMaxPos() { return to - from; } @Override public KEY_GENERIC_TYPE NEXT_KEY() { if (! hasNext()) throw new NoSuchElementException(); return a[from + (lastReturned = pos++)]; } @@ -663,9 +671,11 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; final int max = to - from; - while(pos < max) { - action.accept(a[from + (lastReturned = pos++)]); + for (int i = pos; i < max; i++) { + action.accept(a[i]); } + pos = max; + lastReturned = max - 1; } } @@ -686,12 +696,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } @Override - protected final int getMaxPosFromBackingStore() { return to; } + protected int getMaxPosFromBackingStore() { return to; } @Override - protected final KEY_GENERIC_TYPE get(int i) { return a[i]; } + protected KEY_GENERIC_TYPE get(int i) { return a[i]; } @Override - protected final SubListSpliterator makeForSplit(int pos, int maxPos) { + protected SubListSpliterator makeForSplit(int pos, int maxPos) { return new SubListSpliterator(pos, maxPos); } @Override @@ -704,9 +714,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; final int max = getMaxPos(); - while(pos < max) { - action.accept(a[pos++]); + for (int i = pos; i < max; i++) { + action.accept(a[i]); } + pos = max; } } @@ -716,8 +727,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } boolean contentsEquals(KEY_GENERIC_TYPE[] otherA, int otherAFrom, int otherATo) { + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; + final int from = this.from, to = this.to; if (a == otherA && from == otherAFrom && to == otherATo) return true; - if (otherATo - otherAFrom != size()) { + if (otherATo - otherAFrom != to - from) { return false; } int pos = from, otherPos = otherAFrom; @@ -753,6 +766,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #if ! KEYS_USE_REFERENCE_EQUALITY SUPPRESS_WARNINGS_KEY_UNCHECKED int contentsCompareTo(KEY_GENERIC_TYPE[] otherA, int otherAFrom, int otherATo) { + final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; + final int from = this.from, to = this.to; #if KEYS_PRIMITIVE // Can't make this assumption for reference types in case we have a goofy Comparable that doesn't compare itself equal if (a == otherA && from == otherAFrom && to == otherATo) return 0; #endif @@ -791,7 +806,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public LIST KEY_GENERIC subList(int from, int to) { - if (from == 0 && to == size()) return this; + if (from == 0 && to == size) return this; ensureIndex(from); ensureIndex(to); if (from > to) throw new IndexOutOfBoundsException("Start index (" + from + ") is greater than end index (" + to + ")"); @@ -819,11 +834,13 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void removeElements(final int from, final int to) { it.unimi.dsi.fastutil.Arrays.ensureFromTo(size, from, to); + final KEY_TYPE[] a = this.a; System.arraycopy(a, to, a, from, size - to); size -= (to - from); #if KEYS_REFERENCE - int i = to - from; - while(i-- != 0) a[size + i] = null; + for (int i = from; i < to; i++) { + a[i] = null; + } #endif } @@ -840,8 +857,9 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements ensureIndex(index); ARRAYS.ensureOffsetLength(a, offset, length); grow(size + length); - System.arraycopy(this.a, index, this.a, index + length, size - index); - System.arraycopy(a, offset, this.a, index, length); + final KEY_GENERIC_TYPE[] thisA = this.a; + System.arraycopy(thisA, index, thisA, index + length, size - index); + System.arraycopy(a, offset, thisA, index, length); size += length; } @@ -863,7 +881,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = this.a; - for (int i = 0; i < size; ++i) { + for (int i = 0, max = size; i < max; ++i) { action.accept(a[i]); } } @@ -877,6 +895,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements int n = c.size(); if (n == 0) return false; grow(size + n); + final KEY_GENERIC_TYPE[] a = this.a; System.arraycopy(a, index, a, index + n, size - index); final STD_KEY_ITERATOR KEY_EXTENDS_GENERIC i = c.iterator(); size += n; @@ -891,6 +910,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements final int n = l.size(); if (n == 0) return false; grow(size + n); + final KEY_GENERIC_TYPE[] a = this.a; System.arraycopy(a, index, a, index + n, size - index); l.getElements(0, a, index, n); size += n; @@ -901,6 +921,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public boolean removeAll(final STD_KEY_COLLECTION KEY_GENERIC_WILDCARD c) { final KEY_TYPE[] a = this.a; + final int size = this.size; int j = 0; for(int i = 0; i < size; i++) if (! c.contains(a[i])) a[j++] = a[i]; @@ -908,13 +929,14 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements Arrays.fill(a, j, size, null); #endif final boolean modified = size != j; - size = j; + this.size = j; return modified; } @Override public boolean removeIf(final METHOD_ARG_PREDICATE filter) { final KEY_GENERIC_TYPE[] a = this.a; + final int size = this.size; int j = 0; for(int i = 0; i < size; i++) if (! filter.test(a[i])) a[j++] = a[i]; @@ -922,7 +944,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements Arrays.fill(a, j, size, null); #endif final boolean modified = size != j; - size = j; + this.size = j; return modified; } @@ -930,6 +952,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public KEY_TYPE[] toArray(KEY_TYPE[] a) { + final int size = this.size; if (a == null || a.length < size) a = java.util.Arrays.copyOf(a, size); System.arraycopy(this.a, 0, a, 0, size); return a; @@ -939,7 +962,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public Object[] toArray() { - final int size = size(); + final int size = this.size; // A subtle part of the spec says the returned array must be Object[] exactly. if (size == 0) return it.unimi.dsi.fastutil.objects.ObjectArrays.EMPTY_ARRAY; return Arrays.copyOf(a, size, Object[].class); @@ -948,14 +971,15 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public T[] toArray(T[] a) { + final int size = this.size; if (a == null) { - a = (T[]) new Object[size()]; - } else if (a.length < size()) { - a = (T[]) Array.newInstance(a.getClass().getComponentType(), size()); + a = (T[]) new Object[size]; + } else if (a.length < size) { + a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); } - System.arraycopy(this.a, 0, a, 0, size()); - if (a.length > size()) { - a[size()] = null; + System.arraycopy(this.a, 0, a, 0, size); + if (a.length > size) { + a[size] = null; } return a; } @@ -1001,9 +1025,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - while (pos < size) { - action.accept(a[last = pos++]); + final int max = ARRAY_LIST.this.size; + for (int i = pos; i < max; i++) { + action.accept(a[i]); } + pos = max; + last = max - 1; } @Override public int back(int n) { @@ -1039,7 +1066,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements // Until we split, we will track the size of the list. // Once we split, then we stop updating on structural modifications. // Aka, size is late-binding. - boolean hasSplit = false; + boolean hasSplit; int pos, max; #ifdef TEST @@ -1079,9 +1106,11 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - for (final int max = getWorkingMax(); pos < max; ++pos) { - action.accept(a[pos]); + final int max = getWorkingMax(); + for (int i = pos; i < max; i++) { + action.accept(a[i]); } + pos = max; } @Override @@ -1107,11 +1136,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements // Update instance max with the last seen list size (if needed) before continuing this.max = max; int myNewPos = pos + retLen; - int retMax = myNewPos; int oldPos = pos; this.pos = myNewPos; this.hasSplit = true; - return new Spliterator(oldPos, retMax, true); + return new Spliterator(oldPos, myNewPos, true); } } @@ -1130,7 +1158,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements return new Spliterator(); } - SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public void sort(final KEY_COMPARATOR KEY_SUPER_GENERIC comp) { if (comp == null) { @@ -1152,7 +1179,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public ARRAY_LIST KEY_GENERIC clone() { - ARRAY_LIST KEY_GENERIC cloned = null; + ARRAY_LIST KEY_GENERIC cloned; // Test for fastpath we can do if exactly an ArrayList if (getClass() == ARRAY_LIST.class) { // Preserve backwards compatibility and make new list have Object[] even if it was wrapped from some subclass. @@ -1185,12 +1212,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public boolean equals(final ARRAY_LIST KEY_GENERIC l) { // TODO When minimum version of Java becomes Java 9, use the Arrays.equals which takes bounds, which is vectorized. if (l == this) return true; - int s = size(); + int s = this.size; if (s != l.size()) return false; - final KEY_GENERIC_TYPE[] a1 = a; - final KEY_GENERIC_TYPE[] a2 = l.a; - - if (a1 == a2 && s == l.size()) return true; + final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; + if (a1 == a2) return true; #if KEY_CLASS_Object while(s-- != 0) if (! java.util.Objects.equals(a1[s], a2[s])) return false; @@ -1236,7 +1261,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements */ SUPPRESS_WARNINGS_KEY_UNCHECKED public int compareTo(final ARRAY_LIST KEY_EXTENDS_GENERIC l) { - final int s1 = size(), s2 = l.size(); + final int s1 = size, s2 = l.size(); final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; #if KEYS_PRIMITIVE // Can't make this assumption for reference types in case we have a goofy Comparable that doesn't compare itself equal if (a1 == a2 && s1 == s2) return 0; @@ -1271,15 +1296,17 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); + final int size = this.size; final KEY_GENERIC_TYPE[] a = this.a; - for(int i = 0; i < size; i++) s.WRITE_KEY(a[i]); + for (int i = 0; i < size; i++) s.WRITE_KEY(a[i]); } SUPPRESS_WARNINGS_KEY_UNCHECKED private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); + final int size = this.size; final KEY_GENERIC_TYPE[] a = this.a = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[size]; - for(int i = 0; i < size; i++) a[i] = KEY_GENERIC_CAST s.READ_KEY(); + for (int i = 0; i < size; i++) a[i] = KEY_GENERIC_CAST s.READ_KEY(); } From 9f2985af3536a42cd070baaf057374f5417674d8 Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 13 Feb 2024 16:46:20 +0800 Subject: [PATCH 08/28] Replace array expansion with `Arrays.copyOf` --- drv/ArrayList.drv | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index dab05190..2795b050 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -449,11 +449,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements a = ARRAYS.ensureCapacity(a, capacity, size); #else if (wrapped) a = ARRAYS.ensureCapacity(a, capacity, size); - else { - final Object[] t = new Object[capacity]; - System.arraycopy(a, 0, t, 0, size); - a = (KEY_GENERIC_TYPE[])t; - } + else a = Arrays.copyOf(a, capacity); #endif assert size <= a.length; } @@ -473,11 +469,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements a = ARRAYS.forceCapacity(a, capacity, size); #else if (wrapped) a = ARRAYS.forceCapacity(a, capacity, size); - else { - final Object[] t = new Object[capacity]; - System.arraycopy(a, 0, t, 0, size); - a = (KEY_GENERIC_TYPE[])t; - } + else a = Arrays.copyOf(a, capacity); #endif assert size <= a.length; } From a6c127fe2f9a48bcf37e1bdbca1fb4971b75db19 Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 13 Feb 2024 16:46:47 +0800 Subject: [PATCH 09/28] Revert delete assertions --- drv/ArrayList.drv | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index 2795b050..342a84dd 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -524,6 +524,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #if KEYS_REFERENCE a[size] = null; #endif + assert size <= a.length; return old; } @@ -532,6 +533,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements int index = indexOf(k); if (index == -1) return false; REMOVE_KEY(index); + assert size <= a.length; return true; } @@ -541,6 +543,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements final KEY_GENERIC_TYPE[] a = this.a; final KEY_GENERIC_TYPE old = a[index]; a[index] = k; + assert size <= a.length; return old; } From f5c7cfc9858056c70ed81bef3d18fd54c7b4b84a Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 13 Feb 2024 21:09:26 +0800 Subject: [PATCH 10/28] Fix ArrayList.removeElements --- drv/ArrayList.drv | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index 342a84dd..139f8a14 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -828,15 +828,14 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements */ @Override public void removeElements(final int from, final int to) { + final int size = this.size; it.unimi.dsi.fastutil.Arrays.ensureFromTo(size, from, to); final KEY_TYPE[] a = this.a; System.arraycopy(a, to, a, from, size - to); - size -= (to - from); #if KEYS_REFERENCE - for (int i = from; i < to; i++) { - a[i] = null; - } + Arrays.fill(a, to, size, null); #endif + this.size -= (to - from); } From f5dfbbe15325a47706468233f24dc71a97b89ad3 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Tue, 13 Feb 2024 13:40:03 +0100 Subject: [PATCH 11/28] Added blurb for mouse0w0 --- CHANGES | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGES b/CHANGES index 01ff0e09..1663be22 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,7 @@ +8.5.14 + +- Potential improvements by array caching thanks to mouse0w0@github.com. + 8.5.13 - Thanks to Chanoch Goldfeder for fixing a number of bugs in ImmutableList. From 73f111d29ecaec53da22fc9caff941dcd3730134 Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 13 Feb 2024 22:12:09 +0800 Subject: [PATCH 12/28] Remove redundant suppression --- drv/ArrayList.drv | 2 -- 1 file changed, 2 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index 139f8a14..1f596e9b 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -442,7 +442,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * * @param capacity the new minimum capacity for this array list. */ - SUPPRESS_WARNINGS_KEY_UNCHECKED public void ensureCapacity(final int capacity) { if (capacity <= a.length || (a == ARRAYS.DEFAULT_EMPTY_ARRAY && capacity <= DEFAULT_INITIAL_CAPACITY)) return; #if KEYS_PRIMITIVE @@ -459,7 +458,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * * @param capacity the new minimum capacity for this array list. */ - SUPPRESS_WARNINGS_KEY_UNCHECKED private void grow(int capacity) { if (capacity <= a.length) return; if (a != ARRAYS.DEFAULT_EMPTY_ARRAY) From 7543764667e33e5603885aeb570a69a5a29778b5 Mon Sep 17 00:00:00 2001 From: Mouse Date: Tue, 13 Feb 2024 22:13:15 +0800 Subject: [PATCH 13/28] Revert delete assertions --- drv/ArrayList.drv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index 1f596e9b..c246e8e3 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -541,7 +541,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements final KEY_GENERIC_TYPE[] a = this.a; final KEY_GENERIC_TYPE old = a[index]; a[index] = k; - assert size <= a.length; return old; } @@ -607,6 +606,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements final KEY_GENERIC_TYPE[] t = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[Math.max(n, size)]; System.arraycopy(a, 0, t, 0, size); this.a = t; + assert size <= a.length; } private class SubList extends ABSTRACT_LIST.SUBLIST_RANDOM_ACCESS KEY_GENERIC { From c09089c51ebcbc7788351b05fb8b3591fcf41c38 Mon Sep 17 00:00:00 2001 From: Mouse Date: Thu, 15 Feb 2024 01:18:11 +0800 Subject: [PATCH 14/28] Fix ArrayList.SubListIterator.forEachRemaining --- drv/ArrayList.drv | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index c246e8e3..8d290cc0 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -663,10 +663,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int max = to - from; - for (int i = pos; i < max; i++) { + final int from = this.from, to = this.to; + for (int i = from + pos; i < to; i++) { action.accept(a[i]); } + + final int max = to - from; pos = max; lastReturned = max - 1; } From fa7af7905bf5b45d4cb509a80785338b7ce70a43 Mon Sep 17 00:00:00 2001 From: Mouse Date: Thu, 15 Feb 2024 01:21:24 +0800 Subject: [PATCH 15/28] Change code style of ArrayList --- drv/ArrayList.drv | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index 8d290cc0..a9f8ec13 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -500,7 +500,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public int indexOf(final KEY_TYPE k) { final KEY_TYPE[] a = this.a; - for(int i = 0, max = size; i < max; i++) if (KEY_EQUALS(k, a[i])) return i; + final int size = this.size; + for(int i = 0; i < size; i++) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -632,7 +633,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - for (int i = from, max = to; i < max; ++i) { + final int to = this.to; + for (int i = from; i < to; ++i) { action.accept(a[i]); } } @@ -875,7 +877,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = this.a; - for (int i = 0, max = size; i < max; ++i) { + final int size = this.size; + for (int i = 0; i < size; ++i) { action.accept(a[i]); } } @@ -1019,12 +1022,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int max = ARRAY_LIST.this.size; - for (int i = pos; i < max; i++) { + final int size = ARRAY_LIST.this.size; + for (int i = pos; i < size; i++) { action.accept(a[i]); } - pos = max; - last = max - 1; + pos = size; + last = size - 1; } @Override public int back(int n) { From e8658d7af41ada0560c6ac39f303ea8ee8d3f9a1 Mon Sep 17 00:00:00 2001 From: Mouse Date: Thu, 15 Feb 2024 01:26:27 +0800 Subject: [PATCH 16/28] Fix compilation error in ArrayList.SubListIterator.forEachRemaining --- drv/ArrayList.drv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index a9f8ec13..e70b38e6 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -665,7 +665,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int from = this.from, to = this.to; + final int from = SubList.this.from, to = SubList.this.to; for (int i = from + pos; i < to; i++) { action.accept(a[i]); } From b856e742cd1e446b0d06ddeca1bbf3ea05958007 Mon Sep 17 00:00:00 2001 From: Mouse Date: Thu, 15 Feb 2024 02:00:29 +0800 Subject: [PATCH 17/28] Revert delete assertions --- drv/ArrayList.drv | 1 + 1 file changed, 1 insertion(+) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index e70b38e6..a725d542 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -551,6 +551,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements Arrays.fill(a, 0, size, null); #endif size = 0; + assert size <= a.length; } @Override From e67b24fae817e7508a863f7dfd363f01d869fc4a Mon Sep 17 00:00:00 2001 From: Mouse Date: Thu, 15 Feb 2024 02:15:26 +0800 Subject: [PATCH 18/28] Change reverse iteration to forward iteration in ArrayList.equals --- drv/ArrayList.drv | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index a725d542..a89bfcf0 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -1210,15 +1210,15 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public boolean equals(final ARRAY_LIST KEY_GENERIC l) { // TODO When minimum version of Java becomes Java 9, use the Arrays.equals which takes bounds, which is vectorized. if (l == this) return true; - int s = this.size; - if (s != l.size()) return false; + final int size = this.size; + if (size != l.size()) return false; final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; if (a1 == a2) return true; #if KEY_CLASS_Object - while(s-- != 0) if (! java.util.Objects.equals(a1[s], a2[s])) return false; + for (int i = 0; i < size; i++) if (!java.util.Objects.equals(a1[i], a2[i])) return false; #else - while(s-- != 0) if (a1[s] != a2[s]) return false; + for (int i = 0; i < size; i++) if (a1[i] != a2[i]) return false; #endif return true; } From a439dee15f43283ad538b1a2f6b63155446b3176 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Mon, 10 Jun 2024 09:20:29 +0200 Subject: [PATCH 19/28] Bumped revision --- CHANGES | 3 +++ build.properties | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 1663be22..deff0538 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,9 @@ - Potential improvements by array caching thanks to mouse0w0@github.com. +- Fixed a bug in sublist iterators of immutable lists. Thanks to Barak + Ugav for finding and fixing this bug. + 8.5.13 - Thanks to Chanoch Goldfeder for fixing a number of bugs in ImmutableList. diff --git a/build.properties b/build.properties index 475a63ef..c0a05dd4 100644 --- a/build.properties +++ b/build.properties @@ -3,7 +3,7 @@ javadoc.base=/usr/share/javadoc build.sysclasspath=ignore -version=8.5.13 +version=8.5.14 dist=dist src=src From defd2d1fe36d328824478ef0914184b544bd21d3 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Fri, 14 Jun 2024 13:27:48 +0200 Subject: [PATCH 20/28] Fixed inconsistent behavior and added missing impls in iterator of array-based containers --- CHANGES | 7 +++ drv/ArrayMap.drv | 19 +++++++ drv/ArraySet.drv | 20 ++++--- .../dsi/fastutil/ints/IntSetGuavaTest.java | 1 + .../fastutil/ints/Int2IntArrayMapTest.java | 44 ++++++++++++++++ .../dsi/fastutil/ints/IntArraySetTest.java | 52 +++++++++++++++++-- .../objects/Object2ObjectArrayMapTest.java | 48 +++++++++++++++++ .../fastutil/objects/ObjectArraySetTest.java | 39 ++++++++++++++ 8 files changed, 219 insertions(+), 11 deletions(-) diff --git a/CHANGES b/CHANGES index deff0538..76fd6eff 100644 --- a/CHANGES +++ b/CHANGES @@ -5,6 +5,13 @@ - Fixed a bug in sublist iterators of immutable lists. Thanks to Barak Ugav for finding and fixing this bug. +- Implemented missing skip() and forEachRemaining() methods in array-based + containers. + +- Fixed a bug in array-based containers that would have thrown the wrong + exception and leave the iterator in an inconsistent state when removing + before iterating. Thanks to Michal Frajt for reporting this bug. + 8.5.13 - Thanks to Chanoch Goldfeder for fixing a number of bugs in ImmutableList. diff --git a/drv/ArrayMap.drv b/drv/ArrayMap.drv index c261a1c8..15bb2cbd 100644 --- a/drv/ArrayMap.drv +++ b/drv/ArrayMap.drv @@ -174,6 +174,15 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC #endif } + @Override + public int skip(int n) { + if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n); + n = Math.min(n, size - next); + next += n; + if (n != 0) curr = next - 1; + return n; + } + @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { @@ -220,6 +229,16 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC #endif } + @Override + public int skip(int n) { + if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n); + n = Math.min(n, size - next); + next += n; + if (n != 0) curr = next - 1; + return n; + } + + @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { diff --git a/drv/ArraySet.drv b/drv/ArraySet.drv index b241866d..8d2471e4 100644 --- a/drv/ArraySet.drv +++ b/drv/ArraySet.drv @@ -194,7 +194,7 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j SUPPRESS_WARNINGS_KEY_UNCHECKED public KEY_ITERATOR KEY_GENERIC iterator() { return new KEY_ITERATOR KEY_GENERIC () { - int next = 0; + int curr = -1, next = 0; @Override public boolean hasNext() { return next < size; } @@ -207,6 +207,8 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j @Override public void remove() { + if (curr == -1) throw new IllegalStateException(); + curr = -1; final int tail = size-- - next--; System.arraycopy(a, next + 1, a, next, tail); #if KEYS_REFERENCE @@ -217,15 +219,17 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j @Override public int skip(int n) { if (n < 0) throw new IllegalArgumentException("Argument must be nonnegative: " + n); - final int remaining = size - next; - if (n < remaining) { - next += n; - return n; - } - n = remaining; - next = size; + n = Math.min(n, size - next); + next += n; + if (n != 0) curr = next - 1; return n; } + + @Override + public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { + final KEY_TYPE a[] = ARRAY_SET.this.a; + while (next < size) action.accept(KEY_GENERIC_CAST a[next++]); + } }; } diff --git a/guava/it/unimi/dsi/fastutil/ints/IntSetGuavaTest.java b/guava/it/unimi/dsi/fastutil/ints/IntSetGuavaTest.java index 737576fb..64f0a3c5 100644 --- a/guava/it/unimi/dsi/fastutil/ints/IntSetGuavaTest.java +++ b/guava/it/unimi/dsi/fastutil/ints/IntSetGuavaTest.java @@ -72,4 +72,5 @@ public SampleElements samples() { } }).named(name).withFeatures(CollectionSize.ANY, SetFeature.GENERAL_PURPOSE, CollectionFeature.ALLOWS_NULL_QUERIES, CollectionFeature.RESTRICTS_ELEMENTS, CollectionFeature.SUPPORTS_ADD, CollectionFeature.SUPPORTS_REMOVE, CollectionFeature.SERIALIZABLE, CollectionFeature.REMOVE_OPERATIONS, CollectionFeature.SUPPORTS_ITERATOR_REMOVE).suppressing(toStringTests).createTestSuite(); } + } diff --git a/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java b/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java index c78d670b..ac647fa4 100644 --- a/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java +++ b/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java @@ -52,4 +52,48 @@ public void testValuesRemoveAll() { map.values().removeAll(Collections.singleton(Integer.valueOf(24))); assertTrue(map.isEmpty()); } + + @Test + public void testForEachRemaining() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1, 2, 3, + 4 }, new int[] { 0, + 1, 2, 3, 4 })); + for (int i = 0; i <= s.size(); i++) { + final IntIterator iterator = s.keySet().intIterator(); + final int[] j = new int[1]; + for (j[0] = 0; j[0] < i; j[0]++) iterator.nextInt(); + iterator.forEachRemaining(x -> { + if (x != j[0]++) throw new AssertionError(); + }); + } + } + + @Test(expected = IllegalStateException.class) + public void testSkipZeroAtStart() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1 }, new int[] { 0, + 1 })); + final IntIterator i = s.keySet().intIterator(); + i.skip(0); + i.remove(); + } + + @Test + public void testSkipOneAtStart() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1 }, new int[] { 0, + 1 })); + final IntIterator i = s.keySet().intIterator(); + i.skip(1); + i.remove(); + assertEquals(IntArraySet.ofUnchecked(1), s.keySet()); + } + + @Test + public void testSkipBeyondEnd() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1 }, new int[] { 0, + 1 })); + final IntIterator i = s.keySet().intIterator(); + i.skip(4); + i.remove(); + assertEquals(IntArraySet.ofUnchecked(0), s.keySet()); + } } diff --git a/test/it/unimi/dsi/fastutil/ints/IntArraySetTest.java b/test/it/unimi/dsi/fastutil/ints/IntArraySetTest.java index be0eeafd..302c27f6 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntArraySetTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntArraySetTest.java @@ -51,7 +51,7 @@ public void testNullInEquals() { @Test public void testSet() { - for(int i = 0; i <= 1; i++) { + for (int i = 0; i <= 1; i++) { final IntArraySet s = i == 0 ? new IntArraySet() : new IntArraySet(new int[i]); assertTrue(s.add(1)); assertEquals(1 + i, s.size()); @@ -140,7 +140,7 @@ public void testRemove() { iterator.nextInt(); iterator.nextInt(); iterator.remove(); - assertEquals(44, iterator.nextInt ()); + assertEquals(44, iterator.nextInt()); assertFalse(iterator.hasNext()); assertEquals(new IntArraySet(new int[] { 42, 44 }), set); } @@ -190,11 +190,57 @@ public void testOfUnchekedSingleton() { public void testOfUnchekedDuplicatesNotDetected() { // A IntArraySet in an invalid state that by spec we aren't checking for in this method. final IntArraySet s = IntArraySet.ofUnchecked(0, 0); - assertEquals(new IntArraySet(new int[] { 0 , 0 }), s); + assertEquals(new IntArraySet(new int[] { 0, 0 }), s); } @Test public void testZeroLengthToArray() { assertSame(IntArrays.EMPTY_ARRAY, new IntArraySet().toIntArray()); } + + @Test(expected = IllegalStateException.class) + public void testRemovalAtStart() { + final IntArraySet s = IntArraySet.ofUnchecked(0, 1); + final IntIterator i = s.intIterator(); + i.remove(); + } + + @Test + public void testForEachRemaining() { + final IntArraySet s = IntArraySet.ofUnchecked(0, 1, 2, 3, 4); + for (int i = 0; i <= s.size(); i++) { + final IntIterator iterator = s.intIterator(); + final int[] j = new int[1]; + for(j[0] = 0; j[0] < i; j[0]++) iterator.nextInt(); + iterator.forEachRemaining(x -> { + if (x != j[0]++) throw new AssertionError(); + }); + } + } + + @Test(expected = IllegalStateException.class) + public void testSkipZeroAtStart() { + final IntArraySet s = IntArraySet.ofUnchecked(0, 1); + final IntIterator i = s.intIterator(); + i.skip(0); + i.remove(); + } + + @Test + public void testSkipOneAtStart() { + final IntArraySet s = IntArraySet.ofUnchecked(0, 1); + final IntIterator i = s.intIterator(); + i.skip(1); + i.remove(); + assertEquals(IntArraySet.ofUnchecked(1), s); + } + + @Test + public void testSkipBeyondEnd() { + final IntArraySet s = IntArraySet.ofUnchecked(0, 1); + final IntIterator i = s.intIterator(); + i.skip(4); + i.remove(); + assertEquals(IntArraySet.ofUnchecked(0), s); + } } diff --git a/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java b/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java index bc83549f..271eba34 100644 --- a/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java +++ b/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java @@ -28,6 +28,7 @@ import org.junit.Test; +import it.unimi.dsi.fastutil.ints.IntArraySet; import it.unimi.dsi.fastutil.io.BinIO; public class Object2ObjectArrayMapTest { @@ -154,4 +155,51 @@ public void testIteratorRemove() { assertEquals(Integer.valueOf(3), next.getKey()); assertEquals(Integer.valueOf(3), next.getValue()); } + + @SuppressWarnings("boxing") + @Test + public void testForEachRemaining() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1, 2, 3, 4 }, new Integer[] { 0, 1, 2, 3, 4 })); + for (int i = 0; i <= s.size(); i++) { + final ObjectIterator iterator = s.keySet().iterator(); + final int[] j = new int[1]; + for (j[0] = 0; j[0] < i; j[0]++) iterator.next(); + iterator.forEachRemaining(x -> { + if (x.intValue() != j[0]++) throw new AssertionError(); + }); + } + } + + @SuppressWarnings("boxing") + @Test(expected = IllegalStateException.class) + public void testSkipZeroAtStart() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1 }, new Integer[] { 0, 1 })); + final ObjectIterator i = s.keySet().iterator(); + i.skip(0); + i.remove(); + } + + @SuppressWarnings("boxing") + @Test + public void testSkipOneAtStart() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1 }, new Integer[] { 0, 1 })); + final ObjectIterator i = s.keySet().iterator(); + i.skip(1); + i.remove(); + assertEquals(IntArraySet.ofUnchecked(1), s.keySet()); + } + + @SuppressWarnings("boxing") + @Test + public void testSkipBeyondEnd() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1 }, new Integer[] { 0, 1 })); + final ObjectIterator i = s.keySet().iterator(); + i.skip(4); + i.remove(); + assertEquals(IntArraySet.ofUnchecked(0), s.keySet()); + } } diff --git a/test/it/unimi/dsi/fastutil/objects/ObjectArraySetTest.java b/test/it/unimi/dsi/fastutil/objects/ObjectArraySetTest.java index cd109a26..747a37b6 100644 --- a/test/it/unimi/dsi/fastutil/objects/ObjectArraySetTest.java +++ b/test/it/unimi/dsi/fastutil/objects/ObjectArraySetTest.java @@ -239,4 +239,43 @@ public void testOfUnchekedDuplicatesNotDetected() { public void testZeroLengthToArray() { assertSame(ObjectArrays.EMPTY_ARRAY, new ObjectArraySet().toArray()); } + + @Test(expected = IllegalStateException.class) + public void testRemovalAtStart() { + final ObjectArraySet s = ObjectArraySet.ofUnchecked("0", "1"); + final ObjectIterator i = s.iterator(); + i.remove(); + } + + @Test + public void testForEachRemaining() { + final ObjectArraySet s = ObjectArraySet.ofUnchecked("0", "1", "2", "3", "4"); + for (int i = 0; i <= s.size(); i++) { + final Iterator iterator = s.iterator(); + final int[] j = new int[1]; + for (j[0] = 0; j[0] < i; j[0]++) iterator.next(); + iterator.forEachRemaining(x -> { + if (!x.equals(String.valueOf(j[0]++))) throw new AssertionError(); + }); + } + } + + @Test + public void testSkipOneAtStart() { + final ObjectArraySet s = ObjectArraySet.ofUnchecked("0", "1"); + final ObjectIterator i = s.iterator(); + i.skip(1); + i.remove(); + assertEquals(ObjectArraySet.ofUnchecked("1"), s); + } + + @Test + public void testSkipBeyondEnd() { + final ObjectArraySet s = ObjectArraySet.ofUnchecked("0", "1"); + final ObjectIterator i = s.iterator(); + i.skip(4); + i.remove(); + assertEquals(ObjectArraySet.ofUnchecked("0"), s); + } } + From 3e59d8a9577801e9df866b259e4c9ebfcb9c3923 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Mon, 17 Jun 2024 15:09:35 +0200 Subject: [PATCH 21/28] Fixed several problems with iterators of array-based maps --- CHANGES | 4 + drv/ArrayMap.drv | 136 +++++++++++++++--- drv/ArraySet.drv | 2 +- .../fastutil/ints/Int2IntArrayMapTest.java | 43 +++++- .../fastutil/ints/Int2IntOpenHashMapTest.java | 1 - .../objects/Object2ObjectArrayMapTest.java | 40 ++++++ 6 files changed, 199 insertions(+), 27 deletions(-) diff --git a/CHANGES b/CHANGES index 76fd6eff..77ba1603 100644 --- a/CHANGES +++ b/CHANGES @@ -12,6 +12,10 @@ exception and leave the iterator in an inconsistent state when removing before iterating. Thanks to Michal Frajt for reporting this bug. +- Entry.setValue() now works correctly in all iterators and iterator-like + methods of array-based maps. It was previously throwing an + UnsupportedOperationException. + 8.5.13 - Thanks to Chanoch Goldfeder for fixing a number of bugs in ImmutableList. diff --git a/drv/ArrayMap.drv b/drv/ArrayMap.drv index 15bb2cbd..61500428 100644 --- a/drv/ArrayMap.drv +++ b/drv/ArrayMap.drv @@ -140,13 +140,113 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC if (size > key.length) throw new IllegalArgumentException("The provided size (" + size + ") is larger than or equal to the backing-arrays size (" + key.length + ")"); } - private final class EntrySet extends AbstractObjectSet implements FastEntrySet KEY_VALUE_GENERIC { + /** The entry class for an array map does not record key and value, but + * rather the position in the array of the corresponding entry. This + * is necessary so that calls to {@link java.util.Map.Entry#setValue(Object)} + * are reflected in the map */ + + private final class MapEntry implements MAP.Entry KEY_VALUE_GENERIC, Map.Entry, PAIR KEY_VALUE_GENERIC { + // The array index this entry refers to, or -1 if this entry has been deleted. + int index; + + MapEntry() {} + + MapEntry(final int index) { + this.index = index; + } + + @Override + SUPPRESS_WARNINGS_KEY_UNCHECKED + public KEY_GENERIC_TYPE ENTRY_GET_KEY() { + return KEY_GENERIC_CAST key[index]; + } + + @Override + SUPPRESS_WARNINGS_KEY_UNCHECKED + public KEY_GENERIC_TYPE PAIR_LEFT() { + return KEY_GENERIC_CAST key[index]; + } + + @Override + SUPPRESS_WARNINGS_VALUE_UNCHECKED + public VALUE_GENERIC_TYPE ENTRY_GET_VALUE() { + return VALUE_GENERIC_CAST value[index]; + } + + @Override + SUPPRESS_WARNINGS_VALUE_UNCHECKED + public VALUE_GENERIC_TYPE PAIR_RIGHT() { + return VALUE_GENERIC_CAST value[index]; + } + + @Override + SUPPRESS_WARNINGS_VALUE_UNCHECKED + public VALUE_GENERIC_TYPE setValue(final VALUE_GENERIC_TYPE v) { + final VALUE_GENERIC_TYPE oldValue = VALUE_GENERIC_CAST value[index]; + value[index] = v; + return oldValue; + } + + @Override + public PAIR KEY_VALUE_GENERIC right(final VALUE_GENERIC_TYPE v) { + value[index] = v; + return this; + } + +#if KEYS_PRIMITIVE + /** {@inheritDoc} + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + public KEY_GENERIC_CLASS getKey() { + return KEY2OBJ(key[index]); + } +#endif + +#if VALUES_PRIMITIVE + /** {@inheritDoc} + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + public VALUE_GENERIC_CLASS getValue() { + return VALUE2OBJ(value[index]); + } + + /** {@inheritDoc} + * @deprecated Please use the corresponding type-specific method instead. */ + @Deprecated + @Override + public VALUE_GENERIC_CLASS setValue(final VALUE_GENERIC_CLASS v) { + return VALUE2OBJ(setValue(VALUE_CLASS2TYPE(v))); + } +#endif + + @SuppressWarnings("unchecked") + @Override + public boolean equals(final Object o) { + if (!(o instanceof Map.Entry)) return false; + Map.Entry e = (Map.Entry)o; + + return KEY_EQUALS(key[index], KEY_CLASS2TYPE(e.getKey())) && VALUE_EQUALS(value[index], VALUE_CLASS2TYPE(e.getValue())); + } + + @Override + public int hashCode() { + return KEY2JAVAHASH(key[index]) ^ VALUE2JAVAHASH(value[index]); + } + + @Override + public String toString() { + return key[index] + "=>" + value[index]; + } + } - // TODO Maybe make this return a list-iterator like the LinkedXHashMaps do? (same for other collection view types) + private final class EntrySet extends AbstractObjectSet implements FastEntrySet KEY_VALUE_GENERIC { @Override public ObjectIterator iterator() { return new ObjectIterator() { + private MapEntry entry; int curr = -1, next = 0; @Override @@ -156,7 +256,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public Entry KEY_VALUE_GENERIC next() { if (! hasNext()) throw new NoSuchElementException(); - return new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[curr = next], VALUE_GENERIC_CAST value[next++]); + return entry = new MapEntry(curr = next++); } @Override @@ -166,6 +266,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC final int tail = size-- - next--; System.arraycopy(key, next + 1, key, next, tail); System.arraycopy(value, next + 1, value, next, tail); + entry.index = -1; #if KEYS_REFERENCE key[size] = null; #endif @@ -186,11 +287,10 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { - final KEY_TYPE[] key = ARRAY_MAP.this.key; - final VALUE_TYPE[] value = ARRAY_MAP.this.value; final int max = size; while (next < max) { - action.accept(new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[curr = next], VALUE_GENERIC_CAST value[next++])); + entry = new MapEntry(curr = next++); + action.accept(entry); } } }; @@ -199,8 +299,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override public ObjectIterator fastIterator() { return new ObjectIterator() { + private MapEntry entry = new MapEntry(); int next = 0, curr = -1; - final BasicEntry KEY_VALUE_GENERIC entry = new BasicEntry KEY_VALUE_GENERIC_DIAMOND (); @Override public boolean hasNext() { return next < size; } @@ -209,8 +309,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public Entry KEY_VALUE_GENERIC next() { if (! hasNext()) throw new NoSuchElementException(); - entry.key = KEY_GENERIC_CAST key[curr = next]; - entry.value = VALUE_GENERIC_CAST value[next++]; + entry.index = curr = next++; return entry; } @@ -221,6 +320,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC final int tail = size-- - next--; System.arraycopy(key, next + 1, key, next, tail); System.arraycopy(value, next + 1, value, next, tail); + entry.index = -1; #if KEYS_REFERENCE key[size] = null; #endif @@ -242,12 +342,9 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEachRemaining(final Consumer action) { - final KEY_TYPE[] key = ARRAY_MAP.this.key; - final VALUE_TYPE[] value = ARRAY_MAP.this.value; final int max = size; while (next < max) { - entry.key = KEY_GENERIC_CAST key[curr = next]; - entry.value = VALUE_GENERIC_CAST value[next++]; + entry.index = curr = next++; action.accept(entry); } } @@ -271,7 +368,7 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED protected final MAP.Entry KEY_VALUE_GENERIC get(int location) { - return new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[location], VALUE_GENERIC_CAST value[location]); + return new MapEntry(location); } @Override @@ -289,10 +386,8 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void forEach(final Consumer action) { - final KEY_TYPE[] key = ARRAY_MAP.this.key; - final VALUE_TYPE[] value = ARRAY_MAP.this.value; for (int i = 0, max = size; i < max; ++i) { - action.accept(new ABSTRACT_MAP.BasicEntry KEY_VALUE_GENERIC_DIAMOND(KEY_GENERIC_CAST key[i], VALUE_GENERIC_CAST value[i])); + action.accept(new MapEntry(i)); } } @@ -300,12 +395,9 @@ public class ARRAY_MAP KEY_VALUE_GENERIC extends ABSTRACT_MAP KEY_VALUE_GENERIC @Override SUPPRESS_WARNINGS_KEY_VALUE_UNCHECKED public void fastForEach(final Consumer action) { - final KEY_TYPE[] key = ARRAY_MAP.this.key; - final VALUE_TYPE[] value = ARRAY_MAP.this.value; - final BasicEntry KEY_VALUE_GENERIC entry = new BasicEntry KEY_VALUE_GENERIC_DIAMOND (); + final MapEntry entry = new MapEntry(); for (int i = 0, max = size; i < max; ++i) { - entry.key = KEY_GENERIC_CAST key[i]; - entry.value = VALUE_GENERIC_CAST value[i]; + entry.index = i; action.accept(entry); } } diff --git a/drv/ArraySet.drv b/drv/ArraySet.drv index 8d2471e4..e90a184c 100644 --- a/drv/ArraySet.drv +++ b/drv/ArraySet.drv @@ -202,7 +202,7 @@ public class ARRAY_SET KEY_GENERIC extends ABSTRACT_SET KEY_GENERIC implements j @Override public KEY_GENERIC_TYPE NEXT_KEY() { if (! hasNext()) throw new NoSuchElementException(); - return KEY_GENERIC_CAST a[next++]; + return KEY_GENERIC_CAST a[curr = next++]; } @Override diff --git a/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java b/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java index ac647fa4..4f7642fe 100644 --- a/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java +++ b/test/it/unimi/dsi/fastutil/ints/Int2IntArrayMapTest.java @@ -24,10 +24,12 @@ import org.junit.Test; +import it.unimi.dsi.fastutil.objects.ObjectIterator; + public class Int2IntArrayMapTest { @Test public void testCopyConstructor() { - final Int2IntOpenHashMap m = new Int2IntOpenHashMap(new int [] {1, 2}, new int[] {3, 4}); + final Int2IntOpenHashMap m = new Int2IntOpenHashMap(new int[] { 1, 2 }, new int[] { 3, 4 }); assertEquals(new Int2IntArrayMap(m), m); assertEquals(new HashMap<>(m), m); } @@ -56,8 +58,7 @@ public void testValuesRemoveAll() { @Test public void testForEachRemaining() { final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1, 2, 3, - 4 }, new int[] { 0, - 1, 2, 3, 4 })); + 4 }, new int[] { 0, 1, 2, 3, 4 })); for (int i = 0; i <= s.size(); i++) { final IntIterator iterator = s.keySet().intIterator(); final int[] j = new int[1]; @@ -96,4 +97,40 @@ public void testSkipBeyondEnd() { i.remove(); assertEquals(IntArraySet.ofUnchecked(0), s.keySet()); } + + @Test + public void testSetValue() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1 }, new int[] { 0, + 1 })); + final ObjectIterator i = s.int2IntEntrySet().iterator(); + final Int2IntMap.Entry e = i.next(); + assertEquals(e.setValue(1), 0); + assertEquals(e.setValue(0), 1); + + final ObjectIterator j = s.int2IntEntrySet().fastIterator(); + final Int2IntMap.Entry f = j.next(); + assertEquals(f.setValue(1), 0); + assertEquals(f.setValue(0), 1); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetValueRemoved() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1 }, new int[] { 0, + 1 })); + final ObjectIterator i = s.int2IntEntrySet().iterator(); + final Int2IntMap.Entry e = i.next(); + i.remove(); + assertEquals(e.setValue(1), 0); + } + + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetValueFastRemoved() { + final Int2IntArrayMap s = new Int2IntArrayMap(new Int2IntLinkedOpenHashMap(new int[] { 0, 1 }, new int[] { 0, + 1 })); + final ObjectIterator j = s.int2IntEntrySet().fastIterator(); + final Int2IntMap.Entry f = j.next(); + j.remove(); + assertEquals(f.setValue(1), 0); + } + } diff --git a/test/it/unimi/dsi/fastutil/ints/Int2IntOpenHashMapTest.java b/test/it/unimi/dsi/fastutil/ints/Int2IntOpenHashMapTest.java index f5788d72..ef24afad 100644 --- a/test/it/unimi/dsi/fastutil/ints/Int2IntOpenHashMapTest.java +++ b/test/it/unimi/dsi/fastutil/ints/Int2IntOpenHashMapTest.java @@ -258,5 +258,4 @@ public void testSetValue() { s.int2IntEntrySet().fastForEach(e -> e.setValue(e.getIntValue() + 20)); for (int i = 0; i < 20; i++) assertEquals(20 + i, s.get(i)); } - } diff --git a/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java b/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java index 271eba34..03408ec5 100644 --- a/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java +++ b/test/it/unimi/dsi/fastutil/objects/Object2ObjectArrayMapTest.java @@ -202,4 +202,44 @@ public void testSkipBeyondEnd() { i.remove(); assertEquals(IntArraySet.ofUnchecked(0), s.keySet()); } + + @SuppressWarnings("boxing") + @Test + public void testSetValue() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1 }, new Integer[] { 0, 1 })); + final ObjectIterator> i = s.entrySet().iterator(); + final Entry e = i.next(); + assertEquals(e.setValue(Integer.valueOf(1)), Integer.valueOf(0)); + assertEquals(e.setValue(Integer.valueOf(0)), Integer.valueOf(1)); + + final ObjectIterator> j = s.object2ObjectEntrySet().fastIterator(); + final Entry f = j.next(); + assertEquals(f.setValue(Integer.valueOf(1)), Integer.valueOf(0)); + assertEquals(f.setValue(Integer.valueOf(0)), Integer.valueOf(1)); + } + + @SuppressWarnings("boxing") + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetValueRemoved() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1 }, new Integer[] { 0, 1 })); + final ObjectIterator> i = s.entrySet().iterator(); + final Entry e = i.next(); + i.remove(); + assertEquals(e.setValue(Integer.valueOf(1)), Integer.valueOf(0)); + + } + + @SuppressWarnings("boxing") + @Test(expected = ArrayIndexOutOfBoundsException.class) + public void testSetValueFastRemoved() { + final Object2ObjectArrayMap s = new Object2ObjectArrayMap<>(new Object2ObjectLinkedOpenHashMap<>(new Integer[] { + 0, 1 }, new Integer[] { 0, 1 })); + + final ObjectIterator> j = s.object2ObjectEntrySet().fastIterator(); + final Entry f = j.next(); + j.remove(); + assertEquals(f.setValue(Integer.valueOf(1)), Integer.valueOf(0)); + } } From c031baed7be5fa7590d1fc8af87c820c8966c8e2 Mon Sep 17 00:00:00 2001 From: Barak Ugav Date: Sun, 14 Jan 2024 22:36:47 +0200 Subject: [PATCH 22/28] Primitive Comparator.comparing(keyExtractor), like standrad Comparator --- drv/Comparator.drv | 110 ++++++++++++++++++ .../dsi/fastutil/ints/IntComparatorTest.java | 69 +++++++++++ 2 files changed, 179 insertions(+) create mode 100644 test/it/unimi/dsi/fastutil/ints/IntComparatorTest.java diff --git a/drv/Comparator.drv b/drv/Comparator.drv index 3b805f7d..f63c69a5 100644 --- a/drv/Comparator.drv +++ b/drv/Comparator.drv @@ -18,6 +18,8 @@ package PACKAGE; import java.util.Comparator; +import java.util.Objects; +import java.io.Serializable; /** A type-specific {@link Comparator}; provides methods to compare two primitive types both as objects * and as primitive types. @@ -76,4 +78,112 @@ public interface KEY_COMPARATOR KEY_GENERIC extends Comparator + * The returned comparator is serializable if the specified function is also serializable. + * + * @param keyExtractor the function used to extract the {@link Comparable} sort key + * @return a comparator that compares by an extracted key + * @throws NullPointerException if {@code keyExtractor} is {@code null} + */ +#if KEYS_PRIMITIVE + static > KEY_COMPARATOR KEY_GENERIC comparing(KEY_TO_OBJ_FUNCTION keyExtractor) { +#else + static > KEY_COMPARATOR KEY_GENERIC comparing(KEY_TO_OBJ_FUNCTION keyExtractor) { +#endif + Objects.requireNonNull(keyExtractor); + return (KEY_COMPARATOR KEY_GENERIC & Serializable) + (k1, k2) -> keyExtractor.get(k1).compareTo(keyExtractor.get(k2)); + } + + /** + * Accepts a function that extracts a sort key from a primitive key, and returns a + * comparator that compares by that sort key using the specified {@link Comparator}. + * + *

+ * The returned comparator is serializable if the specified function and comparator are + * both serializable. + * + * @param keyExtractor the function used to extract the sort key + * @param keyComparator the {@code Comparator} used to compare the sort key + * @return a comparator that compares by an extracted key using the specified {@code Comparator} + * @throws NullPointerException if {@code keyExtractor} or {@code keyComparator} are {@code null} + */ +#if KEYS_PRIMITIVE + static > KEY_COMPARATOR KEY_GENERIC comparing(KEY_TO_OBJ_FUNCTION keyExtractor, Comparator keyComparator) { +#else + static > KEY_COMPARATOR KEY_GENERIC comparing(KEY_TO_OBJ_FUNCTION keyExtractor, Comparator keyComparator) { +#endif + Objects.requireNonNull(keyExtractor); + Objects.requireNonNull(keyComparator); + return (KEY_COMPARATOR KEY_GENERIC & Serializable) + (k1, k2) -> keyComparator.compare(keyExtractor.get(k1), keyExtractor.get(k2)); + } + + /** + * Accepts a function that extracts an {@code int} sort key from a primitive key, + * and returns a comparator that compares by that sort key. + * + *

+ * The returned comparator is serializable if the specified function + * is also serializable. + * + * @param keyExtractor the function used to extract the integer sort key + * @return a comparator that compares by an extracted key + * @throws NullPointerException if {@code keyExtractor} is {@code null} + */ + static KEY_GENERIC KEY_COMPARATOR KEY_GENERIC comparingInt(KEY_TO_INT_FUNCTION KEY_SUPER_GENERIC keyExtractor) { + Objects.requireNonNull(keyExtractor); + return (KEY_COMPARATOR KEY_GENERIC & Serializable) + (k1, k2) -> Integer.compare(keyExtractor.get(k1), keyExtractor.get(k2)); + } + + /** + * Accepts a function that extracts an {@code long} sort key from a primitive key, + * and returns a comparator that compares by that sort key. + * + *

+ * The returned comparator is serializable if the specified function + * is also serializable. + * + * @param keyExtractor the function used to extract the long sort key + * @return a comparator that compares by an extracted key + * @throws NullPointerException if {@code keyExtractor} is {@code null} + */ + static KEY_GENERIC KEY_COMPARATOR KEY_GENERIC comparingLong(KEY_TO_LONG_FUNCTION KEY_SUPER_GENERIC keyExtractor) { + Objects.requireNonNull(keyExtractor); + return (KEY_COMPARATOR KEY_GENERIC & Serializable) + (k1, k2) -> Long.compare(keyExtractor.get(k1), keyExtractor.get(k2)); + } + + /** + * Accepts a function that extracts an {@code double} sort key from a primitive key, + * and returns a comparator that compares by that sort key. + * + *

+ * The returned comparator is serializable if the specified function + * is also serializable. + * + * @param keyExtractor the function used to extract the double sort key + * @return a comparator that compares by an extracted key + * @throws NullPointerException if {@code keyExtractor} is {@code null} + */ + static KEY_GENERIC KEY_COMPARATOR KEY_GENERIC comparingDouble(KEY_TO_DOUBLE_FUNCTION KEY_SUPER_GENERIC keyExtractor) { + Objects.requireNonNull(keyExtractor); + return (KEY_COMPARATOR KEY_GENERIC & Serializable) + (k1, k2) -> Double.compare(keyExtractor.get(k1), keyExtractor.get(k2)); + } + } diff --git a/test/it/unimi/dsi/fastutil/ints/IntComparatorTest.java b/test/it/unimi/dsi/fastutil/ints/IntComparatorTest.java new file mode 100644 index 00000000..45a1c255 --- /dev/null +++ b/test/it/unimi/dsi/fastutil/ints/IntComparatorTest.java @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2003-2024 Barak Ugav and Sebastiano Vigna + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package it.unimi.dsi.fastutil.ints; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +public class IntComparatorTest { + + @Test + public void comparing() { + String[] array = new String[] { "68", "98", "30", "62", "81", "61", "80", "63", "62", "77", "10", "95", "40", + "73", "55", "45", "16", "10", "86", "28", "79", "44", "52", "92", "98", "28", "88", "70", "70", "10" }; + IntComparator c = IntComparator.comparing(i -> array[i]); + for (int i = 0; i < array.length; i++) { + int j = ((i + 29) * 1337) % array.length; + assertEquals(c.compare(i, j), array[i].compareTo(array[j])); + } + } + + @Test + public void comparingInt() { + int[] array = new int[] { 81, 87, 70, 54, 40, 79, 16, 8, 84, 39, 37, 84, 64, 60, 31, 44, 95, 15, 52, 48, 19, 20, + 75, 31, 46, 61, 38, 27, 32, 84 }; + IntComparator c = IntComparator.comparingInt(i -> array[i]); + for (int i = 0; i < array.length; i++) { + int j = ((i + 17) * 1337) % array.length; + assertEquals(c.compare(i, j), Integer.compare(array[i], array[j])); + } + } + + @Test + public void comparingLong() { + long[] array = new long[] { 26, 49, 49, 24, 15, 71, 10, 88, 78, 4, 42, 79, 75, 69, 63, 16, 71, 47, 54, 39, 89, + 10, 64, 37, 38, 59, 81, 59, 58, 33 }; + IntComparator c = IntComparator.comparingLong(i -> array[i]); + for (int i = 0; i < array.length; i++) { + int j = ((i + 19) * 1337) % array.length; + assertEquals(c.compare(i, j), Long.compare(array[i], array[j])); + } + } + + @Test + public void comparingDouble() { + double[] array = new double[] { 0.61, 0.97, 0.97, 0.75, 0.73, 0.36, 0.72, 0.14, 0.93, 0.18, 0.45, 0.03, 0.62, + 0.05, 0.04, 0.05, 0.38, 0.89, 0., 0.93, 0.83, 0.14, 0.21, 0.79, 0.5, 0.17, 0.46, 0.74, 0.88, 0.94 }; + IntComparator c = IntComparator.comparingDouble(i -> array[i]); + for (int i = 0; i < array.length; i++) { + int j = ((i + 23) * 1337) % array.length; + assertEquals(c.compare(i, j), Double.compare(array[i], array[j])); + } + } + +} From d054bf30dd0c08ca3642bda600b38ebe3f49346a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ampflower=20=F0=9F=8C=BA?= Date: Fri, 19 Jul 2024 22:43:06 -0700 Subject: [PATCH 23/28] Fix error passing oversized array to PrimitiveImmutableList --- drv/ImmutableList.drv | 2 +- test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java | 7 +++++++ test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java | 6 ++++++ 3 files changed, 14 insertions(+), 1 deletion(-) diff --git a/drv/ImmutableList.drv b/drv/ImmutableList.drv index dfc45246..be0b4e23 100644 --- a/drv/ImmutableList.drv +++ b/drv/ImmutableList.drv @@ -274,7 +274,7 @@ public class IMMUTABLE_LIST KEY_GENERIC extends LISTS.ImmutableListBase KEY_GENE @Override public KEY_TYPE[] toArray(KEY_TYPE[] a) { if (a == null || a.length < size()) a = new KEY_TYPE[this.a.length]; - System.arraycopy(this.a, 0, a, 0, a.length); + System.arraycopy(this.a, 0, a, 0, size()); return a; } #else // KEYS_REFERENCE diff --git a/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java b/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java index 50b554ce..3a190568 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java @@ -16,6 +16,7 @@ package it.unimi.dsi.fastutil.ints; +import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotEquals; @@ -605,4 +606,10 @@ public void testLegacyMainMethodTests() throws Exception { public void testZeroLengthToArray() { assertSame(IntArrays.EMPTY_ARRAY, new IntArrayList().toIntArray()); } + + @Test + public void testOversizedToArray() { + final IntArrayList l = IntArrayList.of(0, 1, 2, 3); + assertArrayEquals(new int[]{0, 1, 2, 3, 0}, l.toArray(new int[5])); + } } diff --git a/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java b/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java index 6f970c41..28edfb8a 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java @@ -439,4 +439,10 @@ public void testSubList_testCompareTo_OtherListImpl() { public void testZeroLengthToArray() { assertSame(IntArrays.EMPTY_ARRAY, IntImmutableList.of().toIntArray()); } + + @Test + public void testOversizedToArray() { + final IntImmutableList l = IntImmutableList.of(0, 1, 2, 3); + assertArrayEquals(new int[]{0, 1, 2, 3, 0}, l.toArray(new int[5])); + } } From faa22ac0b26ec1af680ada5eabf94bdedf9c824f Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Sun, 21 Jul 2024 09:24:28 +0200 Subject: [PATCH 24/28] Added missing @Test annotations --- test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java | 2 ++ test/it/unimi/dsi/fastutil/shorts/ShortArrayListTest.java | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java b/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java index 50b554ce..bfcf35f3 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntArrayListTest.java @@ -147,6 +147,7 @@ public void testRemoveUsingSubSublistIterator() { assertEquals(IntArrayList.wrap(new int[] { 24, 86, 42 }), list); } + @Test public void testAddAll() { final IntArrayList l = IntArrayList.wrap(new int[] { 0, 1 }); l.addAll(IntArrayList.wrap(new int[] { 2, 3 } )); @@ -186,6 +187,7 @@ public void testClearSublist() { assertEquals(IntArrayList.wrap(new int[] { 0, 2 }), l); } + @Test public void testSort() { final IntArrayList l = IntArrayList.wrap(new int[] { 4, 2, 1, 3 }); l.sort(null); diff --git a/test/it/unimi/dsi/fastutil/shorts/ShortArrayListTest.java b/test/it/unimi/dsi/fastutil/shorts/ShortArrayListTest.java index 32b9465c..a3901f4e 100644 --- a/test/it/unimi/dsi/fastutil/shorts/ShortArrayListTest.java +++ b/test/it/unimi/dsi/fastutil/shorts/ShortArrayListTest.java @@ -151,6 +151,7 @@ public void testRemoveUsingSubSublistIterator() { assertEquals(ShortArrayList.wrap(new short[] { 24, 86, 42 }), list); } + @Test public void testAddAll() { final ShortArrayList l = ShortArrayList.wrap(new short[] { 0, 1 }); l.addAll(ShortArrayList.wrap(new short[] { 2, 3 } )); @@ -190,6 +191,7 @@ public void testClearSublist() { assertEquals(ShortArrayList.wrap(new short[] { 0, 2 }), l); } + @Test public void testSort() { final ShortArrayList l = ShortArrayList.wrap(new short[] { 4, 2, 1, 3 }); l.sort(null); From b60fdc27a9cd2543ac1babd14a7c1e9afda85197 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Sun, 21 Jul 2024 09:55:14 +0200 Subject: [PATCH 25/28] CHANGES --- CHANGES | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGES b/CHANGES index 77ba1603..5676fd46 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,9 @@ methods of array-based maps. It was previously throwing an UnsupportedOperationException. +- New methods to obtain comparators from key extractors. Thanks to Barak + Ugav for implementing this feature. + 8.5.13 - Thanks to Chanoch Goldfeder for fixing a number of bugs in ImmutableList. From 45f94ffc027a08d3ec39eacdf242bea710e40164 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Sun, 21 Jul 2024 10:19:17 +0200 Subject: [PATCH 26/28] Revert "Optimize ArrayList" --- drv/ArrayList.drv | 169 ++++++++++++++++++++-------------------------- 1 file changed, 72 insertions(+), 97 deletions(-) diff --git a/drv/ArrayList.drv b/drv/ArrayList.drv index a89bfcf0..b2333ef9 100644 --- a/drv/ArrayList.drv +++ b/drv/ArrayList.drv @@ -117,7 +117,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * with {@link #wrap}. */ SUPPRESS_WARNINGS_KEY_UNCHECKED - private static KEY_GENERIC KEY_GENERIC_TYPE[] copyArraySafe(KEY_GENERIC_TYPE[] a, int length) { + private static final KEY_GENERIC KEY_GENERIC_TYPE[] copyArraySafe(KEY_GENERIC_TYPE[] a, int length) { if (length == 0) return KEY_GENERIC_ARRAY_CAST ARRAYS.EMPTY_ARRAY; #if KEYS_PRIMITIVE return java.util.Arrays.copyOf(a, length); @@ -126,7 +126,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #endif } - private static KEY_GENERIC KEY_GENERIC_TYPE[] copyArrayFromSafe(ARRAY_LIST KEY_GENERIC l) { + private static final KEY_GENERIC KEY_GENERIC_TYPE[] copyArrayFromSafe(ARRAY_LIST KEY_GENERIC l) { return copyArraySafe(l.a, l.size); } @@ -442,13 +442,20 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * * @param capacity the new minimum capacity for this array list. */ + SUPPRESS_WARNINGS_KEY_UNCHECKED public void ensureCapacity(final int capacity) { if (capacity <= a.length || (a == ARRAYS.DEFAULT_EMPTY_ARRAY && capacity <= DEFAULT_INITIAL_CAPACITY)) return; #if KEYS_PRIMITIVE a = ARRAYS.ensureCapacity(a, capacity, size); #else if (wrapped) a = ARRAYS.ensureCapacity(a, capacity, size); - else a = Arrays.copyOf(a, capacity); + else { + if (capacity > a.length) { + final Object[] t = new Object[capacity]; + System.arraycopy(a, 0, t, 0, size); + a = (KEY_GENERIC_TYPE[])t; + } + } #endif assert size <= a.length; } @@ -458,6 +465,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements * * @param capacity the new minimum capacity for this array list. */ + SUPPRESS_WARNINGS_KEY_UNCHECKED private void grow(int capacity) { if (capacity <= a.length) return; if (a != ARRAYS.DEFAULT_EMPTY_ARRAY) @@ -467,7 +475,11 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements a = ARRAYS.forceCapacity(a, capacity, size); #else if (wrapped) a = ARRAYS.forceCapacity(a, capacity, size); - else a = Arrays.copyOf(a, capacity); + else { + final Object[] t = new Object[capacity]; + System.arraycopy(a, 0, t, 0, size); + a = (KEY_GENERIC_TYPE[])t; + } #endif assert size <= a.length; } @@ -476,7 +488,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public void add(final int index, final KEY_GENERIC_TYPE k) { ensureIndex(index); grow(size + 1); - final KEY_GENERIC_TYPE[] a = this.a; if (index != size) System.arraycopy(a, index, a, index + 1, size - index); a[index] = k; size++; @@ -500,7 +511,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public int indexOf(final KEY_TYPE k) { final KEY_TYPE[] a = this.a; - final int size = this.size; for(int i = 0; i < size; i++) if (KEY_EQUALS(k, a[i])) return i; return -1; } @@ -518,7 +528,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")"); final KEY_GENERIC_TYPE[] a = this.a; final KEY_GENERIC_TYPE old = a[index]; - final int size = --this.size; + size--; if (index != size) System.arraycopy(a, index + 1, a, index, size - index); #if KEYS_REFERENCE a[size] = null; @@ -539,8 +549,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public KEY_GENERIC_TYPE set(final int index, final KEY_GENERIC_TYPE k) { if (index >= size) throw new IndexOutOfBoundsException("Index (" + index + ") is greater than or equal to list size (" + size + ")"); - final KEY_GENERIC_TYPE[] a = this.a; - final KEY_GENERIC_TYPE old = a[index]; + KEY_GENERIC_TYPE old = a[index]; a[index] = k; return old; } @@ -561,12 +570,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void size(final int size) { - KEY_GENERIC_TYPE[] a = this.a; - final int oldSize = this.size; - if (size > a.length) a = this.a = ARRAYS.forceCapacity(a, size, oldSize); - if (size > oldSize) Arrays.fill(a, oldSize, size, KEY_NULL); + if (size > a.length) a = ARRAYS.forceCapacity(a, size, this.size); + if (size > this.size) Arrays.fill(a, this.size, size, KEY_NULL); #if KEYS_REFERENCE - else Arrays.fill(a, size, oldSize, KEY_NULL); + else Arrays.fill(a, size, this.size, KEY_NULL); #endif this.size = size; } @@ -602,12 +609,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements SUPPRESS_WARNINGS_KEY_UNCHECKED public void trim(final int n) { // TODO: use Arrays.trim() and preserve type only if necessary - final KEY_GENERIC_TYPE[] a = this.a; - final int size = this.size; if (n >= a.length || size == a.length) return; - final KEY_GENERIC_TYPE[] t = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[Math.max(n, size)]; + final KEY_GENERIC_TYPE t[] = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[Math.max(n, size)]; System.arraycopy(a, 0, t, 0, size); - this.a = t; + a = t; assert size <= a.length; } @@ -631,15 +636,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements return a[i + from]; } - @Override - public void forEach(final METHOD_ARG_KEY_CONSUMER action) { - final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int to = this.to; - for (int i = from; i < to; ++i) { - action.accept(a[i]); - } - } - private final class SubListIterator extends ITERATORS.AbstractIndexBasedListIterator KEY_GENERIC { // We are using pos == 0 to be 0 relative to SubList.from (meaning you need to do a[from + i] when accessing array). @@ -648,15 +644,15 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } @Override - protected KEY_GENERIC_TYPE get(int i) { return a[from + i]; } + protected final KEY_GENERIC_TYPE get(int i) { return a[from + i]; } @Override - protected void add(int i, KEY_GENERIC_TYPE k) { SubList.this.add(i, k); } + protected final void add(int i, KEY_GENERIC_TYPE k) { SubList.this.add(i, k); } @Override - protected void set(int i, KEY_GENERIC_TYPE k) { SubList.this.set(i, k); } + protected final void set(int i, KEY_GENERIC_TYPE k) { SubList.this.set(i, k); } @Override - protected void remove(int i) { SubList.this.REMOVE_KEY(i); } + protected final void remove(int i) { SubList.this.REMOVE_KEY(i); } @Override - protected int getMaxPos() { return to - from; } + protected final int getMaxPos() { return to - from; } @Override public KEY_GENERIC_TYPE NEXT_KEY() { if (! hasNext()) throw new NoSuchElementException(); return a[from + (lastReturned = pos++)]; } @@ -666,14 +662,10 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int from = SubList.this.from, to = SubList.this.to; - for (int i = from + pos; i < to; i++) { - action.accept(a[i]); - } - final int max = to - from; - pos = max; - lastReturned = max - 1; + while(pos < max) { + action.accept(a[from + (lastReturned = pos++)]); + } } } @@ -694,12 +686,12 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } @Override - protected int getMaxPosFromBackingStore() { return to; } + protected final int getMaxPosFromBackingStore() { return to; } @Override - protected KEY_GENERIC_TYPE get(int i) { return a[i]; } + protected final KEY_GENERIC_TYPE get(int i) { return a[i]; } @Override - protected SubListSpliterator makeForSplit(int pos, int maxPos) { + protected final SubListSpliterator makeForSplit(int pos, int maxPos) { return new SubListSpliterator(pos, maxPos); } @Override @@ -712,10 +704,9 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; final int max = getMaxPos(); - for (int i = pos; i < max; i++) { - action.accept(a[i]); + while(pos < max) { + action.accept(a[pos++]); } - pos = max; } } @@ -725,10 +716,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements } boolean contentsEquals(KEY_GENERIC_TYPE[] otherA, int otherAFrom, int otherATo) { - final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int from = this.from, to = this.to; if (a == otherA && from == otherAFrom && to == otherATo) return true; - if (otherATo - otherAFrom != to - from) { + if (otherATo - otherAFrom != size()) { return false; } int pos = from, otherPos = otherAFrom; @@ -764,8 +753,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements #if ! KEYS_USE_REFERENCE_EQUALITY SUPPRESS_WARNINGS_KEY_UNCHECKED int contentsCompareTo(KEY_GENERIC_TYPE[] otherA, int otherAFrom, int otherATo) { - final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int from = this.from, to = this.to; #if KEYS_PRIMITIVE // Can't make this assumption for reference types in case we have a goofy Comparable that doesn't compare itself equal if (a == otherA && from == otherAFrom && to == otherATo) return 0; #endif @@ -804,7 +791,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public LIST KEY_GENERIC subList(int from, int to) { - if (from == 0 && to == size) return this; + if (from == 0 && to == size()) return this; ensureIndex(from); ensureIndex(to); if (from > to) throw new IndexOutOfBoundsException("Start index (" + from + ") is greater than end index (" + to + ")"); @@ -831,14 +818,13 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements */ @Override public void removeElements(final int from, final int to) { - final int size = this.size; it.unimi.dsi.fastutil.Arrays.ensureFromTo(size, from, to); - final KEY_TYPE[] a = this.a; System.arraycopy(a, to, a, from, size - to); + size -= (to - from); #if KEYS_REFERENCE - Arrays.fill(a, to, size, null); + int i = to - from; + while(i-- != 0) a[size + i] = null; #endif - this.size -= (to - from); } @@ -854,9 +840,8 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements ensureIndex(index); ARRAYS.ensureOffsetLength(a, offset, length); grow(size + length); - final KEY_GENERIC_TYPE[] thisA = this.a; - System.arraycopy(thisA, index, thisA, index + length, size - index); - System.arraycopy(a, offset, thisA, index, length); + System.arraycopy(this.a, index, this.a, index + length, size - index); + System.arraycopy(a, offset, this.a, index, length); size += length; } @@ -878,7 +863,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEach(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = this.a; - final int size = this.size; for (int i = 0; i < size; ++i) { action.accept(a[i]); } @@ -893,7 +877,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements int n = c.size(); if (n == 0) return false; grow(size + n); - final KEY_GENERIC_TYPE[] a = this.a; System.arraycopy(a, index, a, index + n, size - index); final STD_KEY_ITERATOR KEY_EXTENDS_GENERIC i = c.iterator(); size += n; @@ -908,7 +891,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements final int n = l.size(); if (n == 0) return false; grow(size + n); - final KEY_GENERIC_TYPE[] a = this.a; System.arraycopy(a, index, a, index + n, size - index); l.getElements(0, a, index, n); size += n; @@ -919,7 +901,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public boolean removeAll(final STD_KEY_COLLECTION KEY_GENERIC_WILDCARD c) { final KEY_TYPE[] a = this.a; - final int size = this.size; int j = 0; for(int i = 0; i < size; i++) if (! c.contains(a[i])) a[j++] = a[i]; @@ -927,14 +908,13 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements Arrays.fill(a, j, size, null); #endif final boolean modified = size != j; - this.size = j; + size = j; return modified; } @Override public boolean removeIf(final METHOD_ARG_PREDICATE filter) { final KEY_GENERIC_TYPE[] a = this.a; - final int size = this.size; int j = 0; for(int i = 0; i < size; i++) if (! filter.test(a[i])) a[j++] = a[i]; @@ -942,7 +922,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements Arrays.fill(a, j, size, null); #endif final boolean modified = size != j; - this.size = j; + size = j; return modified; } @@ -950,7 +930,6 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public KEY_TYPE[] toArray(KEY_TYPE[] a) { - final int size = this.size; if (a == null || a.length < size) a = java.util.Arrays.copyOf(a, size); System.arraycopy(this.a, 0, a, 0, size); return a; @@ -960,7 +939,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public Object[] toArray() { - final int size = this.size; + final int size = size(); // A subtle part of the spec says the returned array must be Object[] exactly. if (size == 0) return it.unimi.dsi.fastutil.objects.ObjectArrays.EMPTY_ARRAY; return Arrays.copyOf(a, size, Object[].class); @@ -969,15 +948,14 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public T[] toArray(T[] a) { - final int size = this.size; if (a == null) { - a = (T[]) new Object[size]; - } else if (a.length < size) { - a = (T[]) Array.newInstance(a.getClass().getComponentType(), size); + a = (T[]) new Object[size()]; + } else if (a.length < size()) { + a = (T[]) Array.newInstance(a.getClass().getComponentType(), size()); } - System.arraycopy(this.a, 0, a, 0, size); - if (a.length > size) { - a[size] = null; + System.arraycopy(this.a, 0, a, 0, size()); + if (a.length > size()) { + a[size()] = null; } return a; } @@ -1023,12 +1001,9 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int size = ARRAY_LIST.this.size; - for (int i = pos; i < size; i++) { - action.accept(a[i]); + while (pos < size) { + action.accept(a[last = pos++]); } - pos = size; - last = size - 1; } @Override public int back(int n) { @@ -1064,7 +1039,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements // Until we split, we will track the size of the list. // Once we split, then we stop updating on structural modifications. // Aka, size is late-binding. - boolean hasSplit; + boolean hasSplit = false; int pos, max; #ifdef TEST @@ -1104,11 +1079,9 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override public void forEachRemaining(final METHOD_ARG_KEY_CONSUMER action) { final KEY_GENERIC_TYPE[] a = ARRAY_LIST.this.a; - final int max = getWorkingMax(); - for (int i = pos; i < max; i++) { - action.accept(a[i]); + for (final int max = getWorkingMax(); pos < max; ++pos) { + action.accept(a[pos]); } - pos = max; } @Override @@ -1134,10 +1107,11 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements // Update instance max with the last seen list size (if needed) before continuing this.max = max; int myNewPos = pos + retLen; + int retMax = myNewPos; int oldPos = pos; this.pos = myNewPos; this.hasSplit = true; - return new Spliterator(oldPos, myNewPos, true); + return new Spliterator(oldPos, retMax, true); } } @@ -1156,6 +1130,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements return new Spliterator(); } + SUPPRESS_WARNINGS_KEY_UNCHECKED @Override public void sort(final KEY_COMPARATOR KEY_SUPER_GENERIC comp) { if (comp == null) { @@ -1177,7 +1152,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements @Override SUPPRESS_WARNINGS_KEY_UNCHECKED public ARRAY_LIST KEY_GENERIC clone() { - ARRAY_LIST KEY_GENERIC cloned; + ARRAY_LIST KEY_GENERIC cloned = null; // Test for fastpath we can do if exactly an ArrayList if (getClass() == ARRAY_LIST.class) { // Preserve backwards compatibility and make new list have Object[] even if it was wrapped from some subclass. @@ -1210,15 +1185,17 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements public boolean equals(final ARRAY_LIST KEY_GENERIC l) { // TODO When minimum version of Java becomes Java 9, use the Arrays.equals which takes bounds, which is vectorized. if (l == this) return true; - final int size = this.size; - if (size != l.size()) return false; - final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; - if (a1 == a2) return true; + int s = size(); + if (s != l.size()) return false; + final KEY_GENERIC_TYPE[] a1 = a; + final KEY_GENERIC_TYPE[] a2 = l.a; + + if (a1 == a2 && s == l.size()) return true; #if KEY_CLASS_Object - for (int i = 0; i < size; i++) if (!java.util.Objects.equals(a1[i], a2[i])) return false; + while(s-- != 0) if (! java.util.Objects.equals(a1[s], a2[s])) return false; #else - for (int i = 0; i < size; i++) if (a1[i] != a2[i]) return false; + while(s-- != 0) if (a1[s] != a2[s]) return false; #endif return true; } @@ -1259,7 +1236,7 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements */ SUPPRESS_WARNINGS_KEY_UNCHECKED public int compareTo(final ARRAY_LIST KEY_EXTENDS_GENERIC l) { - final int s1 = size, s2 = l.size(); + final int s1 = size(), s2 = l.size(); final KEY_GENERIC_TYPE[] a1 = a, a2 = l.a; #if KEYS_PRIMITIVE // Can't make this assumption for reference types in case we have a goofy Comparable that doesn't compare itself equal if (a1 == a2 && s1 == s2) return 0; @@ -1294,17 +1271,15 @@ public class ARRAY_LIST KEY_GENERIC extends ABSTRACT_LIST KEY_GENERIC implements private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { s.defaultWriteObject(); - final int size = this.size; final KEY_GENERIC_TYPE[] a = this.a; - for (int i = 0; i < size; i++) s.WRITE_KEY(a[i]); + for(int i = 0; i < size; i++) s.WRITE_KEY(a[i]); } SUPPRESS_WARNINGS_KEY_UNCHECKED private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { s.defaultReadObject(); - final int size = this.size; final KEY_GENERIC_TYPE[] a = this.a = KEY_GENERIC_ARRAY_CAST new KEY_TYPE[size]; - for (int i = 0; i < size; i++) a[i] = KEY_GENERIC_CAST s.READ_KEY(); + for(int i = 0; i < size; i++) a[i] = KEY_GENERIC_CAST s.READ_KEY(); } From d75a9af680a1c2e10eb820320447b6858aefcd44 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Wed, 16 Oct 2024 22:54:30 +0200 Subject: [PATCH 27/28] Fixed docs --- src/it/unimi/dsi/fastutil/Arrays.java | 2 +- test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/it/unimi/dsi/fastutil/Arrays.java b/src/it/unimi/dsi/fastutil/Arrays.java index 96d0a621..ac8fdbba 100644 --- a/src/it/unimi/dsi/fastutil/Arrays.java +++ b/src/it/unimi/dsi/fastutil/Arrays.java @@ -62,7 +62,7 @@ private Arrays() {} * * @param arrayLength an array length (must be nonnegative). * @param from a start index (inclusive). - * @param to an end index (inclusive). + * @param to an end index (exclusive). * @throws IllegalArgumentException if {@code from} is greater than {@code to}. * @throws ArrayIndexOutOfBoundsException if {@code from} or {@code to} are greater than * {@code arrayLength} or negative. diff --git a/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java b/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java index 28edfb8a..0a521a2a 100644 --- a/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java +++ b/test/it/unimi/dsi/fastutil/ints/IntImmutableListTest.java @@ -111,7 +111,7 @@ public void testToListWithExpectedSize_parallel() { transformed = IntImmutableList.toListWithExpectedSize(baseList.intParallelStream().map(i -> i + 40), 50000); assertEquals(expectedList, transformed); } - + @Test public void testListIterator_testIteration() { final IntImmutableList l = IntImmutableList.of(0, 1, 2, 3); @@ -353,7 +353,7 @@ public void testSublistListIterator_testForEachRemaining() { final IntImmutableList l = IntImmutableList.of(0, 1, 2, 3, 4, 5); final IntList sl = l.subList(1, 4); final IntListIterator li = sl.listIterator(1); - IntList consumer = new IntArrayList(); + final IntList consumer = new IntArrayList(); li.forEachRemaining(consumer::add); assertEquals(consumer, IntList.of(2, 3)); } From 355d8ebfaf2bcc4032cc1c25bec9f3fe827f8175 Mon Sep 17 00:00:00 2001 From: Sebastiano Vigna Date: Sat, 19 Oct 2024 18:14:09 +0200 Subject: [PATCH 28/28] Fixed a long-standing bug in computation of backing-array sizes --- CHANGES | 8 ++++++ build.properties | 2 +- src/it/unimi/dsi/fastutil/HashCommon.java | 32 +++++++++++++++++------ 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/CHANGES b/CHANGES index 5676fd46..6d6817d0 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,11 @@ +8.5.15 + +- Fixed a very long-standing subtle bug that was causing unnecessary + rehashings on large tables. Maximum fills and backing-array sizes + were computed using float precision, rather than double precision, + making it impossible to represent all possible sizes exactly. Thanks + to Captain-S0L0 for reporting this bug. + 8.5.14 - Potential improvements by array caching thanks to mouse0w0@github.com. diff --git a/build.properties b/build.properties index c0a05dd4..d22ec0dd 100644 --- a/build.properties +++ b/build.properties @@ -3,7 +3,7 @@ javadoc.base=/usr/share/javadoc build.sysclasspath=ignore -version=8.5.14 +version=8.5.15 dist=dist src=src diff --git a/src/it/unimi/dsi/fastutil/HashCommon.java b/src/it/unimi/dsi/fastutil/HashCommon.java index 59c6e157..a1f9b2b2 100644 --- a/src/it/unimi/dsi/fastutil/HashCommon.java +++ b/src/it/unimi/dsi/fastutil/HashCommon.java @@ -156,7 +156,7 @@ public static int long2int(final long l) { * @param x an integer smaller than or equal to 230. * @return the least power of two greater than or equal to the specified value. */ - public static int nextPowerOfTwo(int x) { + public static int nextPowerOfTwo(final int x) { return 1 << (32 - Integer.numberOfLeadingZeros(x - 1)); } @@ -167,7 +167,7 @@ public static int nextPowerOfTwo(int x) { * @param x a long integer smaller than or equal to 262. * @return the least power of two greater than or equal to the specified value. */ - public static long nextPowerOfTwo(long x) { + public static long nextPowerOfTwo(final long x) { return 1L << (64 - Long.numberOfLeadingZeros(x - 1)); } @@ -180,8 +180,12 @@ public static long nextPowerOfTwo(long x) { */ public static int maxFill(final int n, final float f) { /* We must guarantee that there is always at least - * one free entry (even with pathological load factors). */ - return Math.min((int)Math.ceil(n * f), n - 1); + * one free entry (even with pathological load factors). + * + * The cast to double is essential to avoid a precision + * loss due to a cast to float before the call to Math.ceil. + */ + return Math.min((int)Math.ceil(n * (double)f), n - 1); } /** Returns the maximum number of entries that can be filled before rehashing. @@ -192,8 +196,12 @@ public static int maxFill(final int n, final float f) { */ public static long maxFill(final long n, final float f) { /* We must guarantee that there is always at least - * one free entry (even with pathological load factors). */ - return Math.min((long)Math.ceil(n * f), n - 1); + * one free entry (even with pathological load factors). + * + * The cast to double is essential to avoid a precision + * loss due to a cast to float before the call to Math.ceil. + */ + return Math.min((long)Math.ceil(n * (double)f), n - 1); } /** Returns the least power of two smaller than or equal to 230 and larger than or equal to {@code Math.ceil(expected / f)}. @@ -204,7 +212,11 @@ public static long maxFill(final long n, final float f) { * @throws IllegalArgumentException if the necessary size is larger than 230. */ public static int arraySize(final int expected, final float f) { - final long s = Math.max(2, nextPowerOfTwo((long)Math.ceil(expected / f))); + /* + * The cast to double is essential to avoid a precision + * loss due to a cast to float before the call to Math.ceil. + */ + final long s = Math.max(2, nextPowerOfTwo((long)Math.ceil(expected / (double)f))); if (s > (1 << 30)) throw new IllegalArgumentException("Too large (" + expected + " expected elements with load factor " + f + ")"); return (int)s; } @@ -216,6 +228,10 @@ public static int arraySize(final int expected, final float f) { * @return the minimum possible size for a backing big array. */ public static long bigArraySize(final long expected, final float f) { - return nextPowerOfTwo((long)Math.ceil(expected / f)); + /* + * The cast to double is essential to avoid a precision + * loss due to a cast to float before the call to Math.ceil. + */ + return nextPowerOfTwo((long)Math.ceil(expected / (double)f)); } }