This is an automated email from the ASF dual-hosted git repository. paulk pushed a commit to branch GROOVY_4_0_X in repository https://gitbox.apache.org/repos/asf/groovy.git
commit bd45d5b86033b38c427df6a14646884f1760d674 Author: Paul King <[email protected]> AuthorDate: Sun Aug 14 23:10:08 2022 +1000 GROOVY-10710: operator == is slow when comparing primitive arrays and lists (port to 4_0_X) --- .../groovy/runtime/DefaultGroovyMethods.java | 16 +- .../typehandling/DefaultTypeTransformation.java | 181 ++++++++++++++++++++- .../DefaultTypeTransformationTest.groovy | 22 +++ 3 files changed, 209 insertions(+), 10 deletions(-) diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java index 9402610ac8..c5e9b65a92 100644 --- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java +++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java @@ -14975,7 +14975,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Byte> toSet(byte[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -14988,7 +14988,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Boolean> toSet(boolean[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -15001,7 +15001,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Character> toSet(char[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -15014,7 +15014,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Short> toSet(short[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -15027,7 +15027,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Integer> toSet(int[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -15040,7 +15040,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Long> toSet(long[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -15053,7 +15053,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Float> toSet(float[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** @@ -15066,7 +15066,7 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport { */ @SuppressWarnings("unchecked") public static Set<Double> toSet(double[] array) { - return toSet(DefaultTypeTransformation.primitiveArrayToList(array)); + return toSet(DefaultTypeTransformation.primitiveArrayToUnmodifiableList(array)); } /** diff --git a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java index ac816bdf57..e19400fc31 100644 --- a/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java +++ b/src/main/java/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformation.java @@ -46,8 +46,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Objects; import java.util.function.Supplier; @@ -559,6 +561,181 @@ public class DefaultTypeTransformation { return list; } + /** + * Allows conversion of arrays into an immutable List view + * + * @param array an array + * @return a List view of the array + */ + public static List primitiveArrayToUnmodifiableList(Object array) { + return new ArrayToUnmodifiableListAdapter(array); + } + + static class ArrayToUnmodifiableListAdapter implements List { + private Object delegate; + + public ArrayToUnmodifiableListAdapter(Object delegate) { + Objects.requireNonNull(delegate); + this.delegate = delegate; + } + + @Override + public int size() { + return Array.getLength(delegate); + } + + @Override + public boolean isEmpty() { + return size() == 0; + } + + @Override + public boolean contains(Object o) { + for (Object next : this) { + if (next.equals(o)) return true; + } + return false; + } + + private class Itr implements Iterator { + private int idx = 0; + + @Override + public boolean hasNext() { + return idx < size(); + } + + @Override + public Object next() { + return get(idx++); + } + } + + @Override + public Iterator iterator() { + return new Itr(); + } + + @Override + public Object get(int index) { + Object item = Array.get(delegate, index); + if (item != null && item.getClass().isArray() && item.getClass().getComponentType().isPrimitive()) { + item = primitiveArrayToUnmodifiableList(item); + } + return item; + } + + @Override + public int indexOf(Object o) { + int idx = 0; + boolean found = false; + while (!found && idx < size()) { + found = get(idx).equals(o); + if (!found) idx++; + } + return found ? idx : -1; + } + + @Override + public int lastIndexOf(Object o) { + int idx = size() - 1; + boolean found = false; + while (!found && idx >= 0) { + found = get(idx).equals(o); + if (!found) idx--; + } + return found ? idx : -1; + } + + @Override + public boolean containsAll(Collection coll) { + for (Object next : coll) { + if (!contains(next)) return false; + } + return true; + } + + @Override + public ListIterator listIterator() { + throw new UnsupportedOperationException(); + } + + @Override + public ListIterator listIterator(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public List subList(int fromIndex, int toIndex) { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray() { + throw new UnsupportedOperationException(); + } + + @Override + public Object[] toArray(Object[] a) { + throw new UnsupportedOperationException(); + } + + @Override + public Object set(int index, Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public void add(int index, Object element) { + throw new UnsupportedOperationException(); + } + + @Override + public Object remove(int index) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(int index, Collection c) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean add(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean remove(Object o) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean addAll(Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeAll(Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean retainAll(Collection coll) { + throw new UnsupportedOperationException(); + } + + @Override + public void clear() { + throw new UnsupportedOperationException(); + } + + @Override + public boolean removeIf(Predicate filter) { + throw new UnsupportedOperationException(); + } + } + public static Object[] primitiveArrayBox(Object array) { int size = Array.getLength(array); Object[] ret = (Object[]) Array.newInstance(ReflectionCache.autoboxType(array.getClass().getComponentType()), size); @@ -661,10 +838,10 @@ public class DefaultTypeTransformation { return compareArrayEqual(left, right); } if (leftClass.isArray() && leftClass.getComponentType().isPrimitive()) { - left = primitiveArrayToList(left); + left = primitiveArrayToUnmodifiableList(left); } if (rightClass.isArray() && rightClass.getComponentType().isPrimitive()) { - right = primitiveArrayToList(right); + right = primitiveArrayToUnmodifiableList(right); } if (left instanceof Object[] && right instanceof List) { return DefaultGroovyMethods.equals((Object[]) left, (List) right); diff --git a/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy b/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy index 5054fb57a3..a889287f71 100644 --- a/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy +++ b/src/test/org/codehaus/groovy/runtime/typehandling/DefaultTypeTransformationTest.groovy @@ -234,6 +234,28 @@ final class DefaultTypeTransformationTest { assert G == N } + @Test + void testPrimitiveArrayToUnmodifiableList() { + int[] nums = [1, 3, 3, 5] + def numList = DefaultTypeTransformation.primitiveArrayToUnmodifiableList(nums) + assert numList.get(1) == 3 + assert numList.contains(1) + assert numList.contains(3) + assert numList.contains(5) + assert !numList.contains(2) + assert numList.indexOf(1) == 0 + assert numList.indexOf(3) == 1 + assert numList.indexOf(5) == 3 + assert numList.indexOf(2) == -1 + assert numList.lastIndexOf(2) == -1 + assert numList.lastIndexOf(3) == 2 + assert numList.containsAll([5,3,1]) + assert !numList.containsAll([5,3,2]) + assert !numList.containsAll([4,3,1]) + assert !numList.isEmpty() + assert numList.size() == 4 + } + //-------------------------------------------------------------------------- private static void checkCompareToSymmetricSmallerThan(a, b) {
