You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2022/08/19 03:37:30 UTC

[groovy] 01/03: GROOVY-10710: operator == is slow when comparing primitive arrays and lists (port to 4_0_X)

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 <pa...@asert.com.au>
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) {