sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject svn commit: r1812822 - in /sis/branches/JDK8/core/sis-utility/src: main/java/org/apache/sis/util/collection/IntegerList.java test/java/org/apache/sis/test/Assert.java test/java/org/apache/sis/util/collection/IntegerListTest.java
Date Sat, 21 Oct 2017 12:34:09 GMT
Author: desruisseaux
Date: Sat Oct 21 12:34:09 2017
New Revision: 1812822

URL: http://svn.apache.org/viewvc?rev=1812822&view=rev
Log:
Override IntegerList.spliterator() and iterator() method with specialized types.

Modified:
    sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java
    sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java

Modified: sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java?rev=1812822&r1=1812821&r2=1812822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/main/java/org/apache/sis/util/collection/IntegerList.java
[UTF-8] Sat Oct 21 12:34:09 2017
@@ -29,6 +29,8 @@ import org.apache.sis.util.ArgumentCheck
 
 // Branch-dependent imports
 import java.util.Spliterator;
+import java.util.PrimitiveIterator;
+import java.util.function.Consumer;
 import java.util.function.IntConsumer;
 import java.util.stream.IntStream;
 import java.util.stream.StreamSupport;
@@ -162,6 +164,8 @@ public class IntegerList extends Abstrac
      * the previous one, then the extra elements are initialized to 0.
      *
      * @param  size  the new size.
+     *
+     * @see #trimToSize()
      */
     public void resize(final int size) {
         ArgumentChecks.ensurePositive("size", size);
@@ -185,7 +189,7 @@ public class IntegerList extends Abstrac
 
     /**
      * Fills the list with the given value.
-     * Every existing values are overwritten from index 0 inclusive up to {@link #size} exclusive.
+     * Every existing values are overwritten from index 0 inclusive up to {@link #size()}
exclusive.
      *
      * @param  value  the value to set.
      */
@@ -283,7 +287,8 @@ public class IntegerList extends Abstrac
 
     /**
      * Returns the element at the given index as the {@code int} primitive type.
-     * This argument does not check argument validity, since it is assumed already done.
+     * This argument does not check argument validity, since the verification is
+     * assumed already done.
      *
      * @param  index  the element index.
      * @return the value at the given index.
@@ -336,7 +341,8 @@ public class IntegerList extends Abstrac
 
     /**
      * Sets the element at the given index as the {@code int} primitive type.
-     * This argument does not check argument validity, since it is assumed already done.
+     * This argument does not check argument validity, since the verification
+     * is assumed already done.
      *
      * @param  index  the element index.
      * @param  value  the value at the given index.
@@ -438,39 +444,29 @@ public class IntegerList extends Abstrac
     }
 
     /**
-     * Trims the capacity of this list to be its current size.
-     */
-    public void trimToSize() {
-        values = ArraysExt.resize(values, length(size));
-    }
-
-    /**
-     * Returns a clone of this list.
+     * Returns an iterator over the elements in this list in increasing index order.
+     * The iterator is <cite>fail-fast</cite> and supports the remove operation.
      *
-     * @return a clone of this list.
+     * @return iterator over the integer values in this list.
+     *
+     * @since 0.8-jdk8
      */
     @Override
-    public IntegerList clone() {
-        final IntegerList clone;
-        try {
-            clone = (IntegerList) super.clone();
-        } catch (CloneNotSupportedException e) {
-            throw new AssertionError(e);
-        }
-        clone.values = clone.values.clone();
-        clone.modCount = 0;
-        return clone;
+    public PrimitiveIterator.OfInt iterator() {
+        return new PrimitiveSpliterator();
     }
 
     /**
-     * Invokes {@link #trimToSize()} before serialization in order to make the stream more
compact.
+     * Returns an spliterator over the elements in this list in increasing index order.
+     * The iterator is <cite>fail-fast</cite>.
      *
-     * @param  out  the output stream where to serialize this list.
-     * @throws IOException if an I/O error occurred while writing.
+     * @return spliterator over the integer values in this list.
+     *
+     * @since 0.8-jdk8
      */
-    private void writeObject(final ObjectOutputStream out) throws IOException {
-        trimToSize();
-        out.defaultWriteObject();
+    @Override
+    public Spliterator.OfInt spliterator() {
+        return new PrimitiveSpliterator();
     }
 
     /**
@@ -479,12 +475,14 @@ public class IntegerList extends Abstrac
      * The returned stream is <cite>fail-fast</cite>, meaning that any modification
to the list
      * while using the stream will cause a {@link ConcurrentModificationException} to be
thrown.
      *
+     * <p>The default implementation creates a sequential stream from {@link #spliterator()}.</p>
+     *
      * @return a stream of values in this list as primitive types.
      *
-     * @since 0.8
+     * @since 0.8-jdk8
      */
     public IntStream ints() {
-        return StreamSupport.intStream(new PrimitiveSpliterator(), false);
+        return StreamSupport.intStream(spliterator(), false);
     }
 
     /**
@@ -492,23 +490,35 @@ public class IntegerList extends Abstrac
      * This spliterator provides a fail-fast way to traverse list content, which means
      * that any alteration to the list content causes a failure of the advance operation
      * with a {@link ConcurrentModificationException}.
+     *
+     * <p>This implementation opportunistically provides an iterator implementation
on
+     * integer values too, but only one of the {@code Iterator} or {@code Spliterator}
+     * API should be used on a given instance.</p>
      */
-    private final class PrimitiveSpliterator implements Spliterator.OfInt {
+    private final class PrimitiveSpliterator implements Spliterator.OfInt, PrimitiveIterator.OfInt
{
         /**
          * Index of the next element to be returned.
          */
-        private int nextIdx;
+        private int nextIndex;
 
         /**
          * The {@link IntegerList#modCount} value as iterator construction time.
          * Used for detecting modification in the backing list during traversal.
          */
-        private final int expectedModCount = modCount;
+        private int expectedModCount;
+
+        /**
+         * Index of the last elements removed by a {@link #remove()} operation.
+         * This is used for checking that {@code remove()} is not invoked twice
+         * before the next advance.
+         */
+        private int lastRemove;
 
         /**
          * Creates a new iterator for the whole content of the backing list.
          */
         PrimitiveSpliterator() {
+            expectedModCount = modCount;
         }
 
         /**
@@ -522,7 +532,7 @@ public class IntegerList extends Abstrac
         }
 
         /**
-         * Returns the number of values in the backing list.
+         * Returns the exact number of values in the backing list.
          */
         @Override
         public long estimateSize() {
@@ -534,32 +544,126 @@ public class IntegerList extends Abstrac
          *       we could use an approach as the one in java standard array lists.
          */
         @Override
-        public OfInt trySplit() {
+        public Spliterator.OfInt trySplit() {
             return null;
         }
 
         /**
+         * Returns {@code true} if there is one more value to return. This method
+         * also ensures that no alteration has happened on the backing list since
+         * the spliterator creation.
+         */
+        @Override
+        public boolean hasNext() {
+            if (modCount == expectedModCount) {
+                return nextIndex < size;
+            } else {
+                throw new ConcurrentModificationException();
+            }
+        }
+
+        /**
+         * Returns the next integer values in iterator order.
+         */
+        @Override
+        public int nextInt() {
+            if (hasNext()) {
+                return getUnchecked(nextIndex++);
+            } else {
+                throw new NoSuchElementException();
+            }
+        }
+
+        /**
          * If a remaining element exists, performs the given action on it and returns {@code
true}.
          * Otherwise returns {@code false}.
          */
         @Override
         public boolean tryAdvance(IntConsumer action) {
-            checkForComodification();
-            final boolean canAdvance = nextIdx < size();
+            final boolean canAdvance = hasNext();
             if (canAdvance) {
-                action.accept(getUnchecked(nextIdx++));
+                action.accept(getUnchecked(nextIndex++));
             }
             return canAdvance;
         }
 
         /**
-         * Ensures that no alteration has happened on the backing list since the
-         * spliterator creation.
+         * Performs the given action on all remaining elements. This implementation
+         * is shared by both {@code Iterator} and {@code Spliterator} interfaces.
+         */
+        @Override
+        public void forEachRemaining(final IntConsumer action) {
+            while (hasNext()) {
+                action.accept(getUnchecked(nextIndex++));
+            }
+        }
+
+        /**
+         * Performs the given action on all remaining elements. This implementation
+         * is shared by both {@code Iterator} and {@code Spliterator} interfaces.
+         */
+        @Override
+        public void forEachRemaining(final Consumer<? super Integer> action) {
+            if (action instanceof IntConsumer) {
+                forEachRemaining((IntConsumer) action);
+            } else while (hasNext()) {
+                action.accept(getUnchecked(nextIndex++));
+            }
+        }
+
+        /**
+         * Removes the last element returned by {@link #nextInt()}.
          */
-        private void checkForComodification() {
+        @Override
+        public void remove() {
+            if (nextIndex < lastRemove || nextIndex > size) {
+                throw new IllegalStateException();
+            }
             if (modCount != expectedModCount) {
                 throw new ConcurrentModificationException();
             }
+            expectedModCount = ++modCount;
+            removeRange(nextIndex - 1, nextIndex);
+            lastRemove = --nextIndex;
+        }
+    }
+
+    /**
+     * Invokes {@link #trimToSize()} before serialization in order to make the stream more
compact.
+     *
+     * @param  out  the output stream where to serialize this list.
+     * @throws IOException if an I/O error occurred while writing.
+     */
+    private void writeObject(final ObjectOutputStream out) throws IOException {
+        trimToSize();
+        out.defaultWriteObject();
+    }
+
+    /**
+     * Trims the capacity of this list to be its current size.
+     *
+     * @see #size()
+     * @see #resize(int)
+     */
+    public void trimToSize() {
+        values = ArraysExt.resize(values, length(size));
+    }
+
+    /**
+     * Returns a clone of this list.
+     *
+     * @return a clone of this list.
+     */
+    @Override
+    public IntegerList clone() {
+        final IntegerList clone;
+        try {
+            clone = (IntegerList) super.clone();
+        } catch (CloneNotSupportedException e) {
+            throw new AssertionError(e);
         }
+        clone.values = Arrays.copyOf(values, length(size));
+        clone.modCount = 0;
+        return clone;
     }
 }

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java?rev=1812822&r1=1812821&r2=1812822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java [UTF-8]
(original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/test/Assert.java [UTF-8]
Sat Oct 21 12:34:09 2017
@@ -19,6 +19,7 @@ package org.apache.sis.test;
 import java.util.Set;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Iterator;
 import java.util.Collection;
 import java.util.Enumeration;
 import java.util.LinkedHashSet;
@@ -37,12 +38,17 @@ import org.apache.sis.util.ComparisonMod
 import org.apache.sis.util.Exceptions;
 import org.apache.sis.util.Classes;
 
+// Branch-dependent imports
+import java.util.stream.Stream;
+import java.util.function.Consumer;
+
 
 /**
  * Assertion methods used by the SIS project in addition of the JUnit and GeoAPI assertions.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @author  Alexis Manin (Geomatys)
+ * @version 0.8
  * @since   0.3
  * @module
  */
@@ -170,6 +176,34 @@ public strictfp class Assert extends org
     }
 
     /**
+     * Verifies that the given stream produces the same values than the given iterator, in
same order.
+     *
+     * @param  <E>       the type of values to test.
+     * @param  expected  the expected values.
+     * @param  actual    the stream to compare with the expected values.
+     *
+     * @since 0.8
+     */
+    public static <E> void assertStreamEquals(final Iterator<E> expected, final
Stream<E> actual) {
+        actual.forEach(new Consumer<E>() {
+            private int count;
+
+            @Override
+            public void accept(final Object value) {
+                if (!expected.hasNext()) {
+                    fail("Expected " + count + " elements, but the stream contains more.");
+                }
+                final Object ex = expected.next();
+                if (!Objects.equals(ex, value)) {
+                    fail("Expected " + ex + " at index " + count + " but got " + value);
+                }
+                count++;
+            }
+        });
+        assertFalse("Unexpected end of stream.", expected.hasNext());
+    }
+
+    /**
      * Asserts that the given set contains the same elements, ignoring order.
      * In case of failure, this method lists the missing or unexpected elements.
      *

Modified: sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java
URL: http://svn.apache.org/viewvc/sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java?rev=1812822&r1=1812821&r2=1812822&view=diff
==============================================================================
--- sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java
[UTF-8] (original)
+++ sis/branches/JDK8/core/sis-utility/src/test/java/org/apache/sis/util/collection/IntegerListTest.java
[UTF-8] Sat Oct 21 12:34:09 2017
@@ -21,6 +21,7 @@ import java.util.ArrayList;
 import java.util.Set;
 import java.util.HashSet;
 import java.util.Collections;
+import java.util.Iterator;
 import java.util.Random;
 import java.util.ConcurrentModificationException;
 import org.apache.sis.test.TestCase;
@@ -47,55 +48,88 @@ import java.util.stream.IntStream;
  */
 public final strictfp class IntegerListTest extends TestCase {
     /**
-     * The list of integers.
+     * The list of integers being tested.
      */
     private IntegerList list;
 
     /**
      * Writes values and read them again for making sure they are the expected ones.
+     * This method tests also split iterators.
      *
      * @param maximalValue  the maximal value allowed.
      */
     private void testReadWrite(final int maximalValue) {
         final Random random = TestUtilities.createRandomNumberGenerator();
-        final int length = 400;
-        // Use half the lenght as initial capacity in order to test dynamic resizing.
+        final int length = 350 + random.nextInt(101);
+        /*
+         * Use half the lenght as initial capacity in order to test dynamic resizing.
+         * Dynamic resizing should happen in one of the call to list.add(value) after
+         * about 200 values.
+         */
         list = new IntegerList(length / 2, maximalValue);
-        assertTrue(list.maximalValue() >= maximalValue);
+        assertTrue("maximalValue()", list.maximalValue() >= maximalValue);
         final List<Integer> copy = new ArrayList<>(length);
         for (int i=0; i<length; i++) {
-            assertEquals(i, list.size());
+            assertEquals("size()", i, list.size());
             final Integer value = nextInt(random, maximalValue);
-            assertTrue(copy.add(value));
-            assertTrue(list.add(value));
+            assertTrue("add(Integer)", copy.add(value));
+            assertTrue("add(Integer)", list.add(value));
         }
-        assertEquals(copy, list);
-        assertEquals(copy.hashCode(), list.hashCode());
+        assertEquals("Comparison with reference implementation", copy, list);
+        assertEquals("hashCode()", copy.hashCode(), list.hashCode());
         /*
-         * Overwrite 1/10 of the values.
+         * Overwrite about 1/10 of the values in both the tested IntegerList and the
+         * reference ArrayList. Then compare the IntegerList against the reference.
          */
-        for (int i=0; i<length; i+=10) {
+        for (int i=0; i<length; i += 8 + random.nextInt(5)) {
             final Integer value = nextInt(random, maximalValue);
             final Integer old = copy.set(i, value);
-            assertNotNull(old);
-            assertEquals(old, list.set(i, value));
+            assertNotNull("set(Integer)", old);
+            assertEquals ("set(Integer)", old, list.set(i, value));
         }
         for (int i=0; i<length; i++) {
-            assertEquals(String.valueOf(i), copy.get(i), list.get(i));
+            if (!copy.get(i).equals(list.get(i))) {
+                fail("Mismatched value at index " + i);
+            }
         }
-        assertEquals(copy, list);
-        assertEquals(copy.hashCode(), list.hashCode());
-        assertNotSame(list, assertSerializedEquals(list));
-        /*
-         * Tests cloning and removal of values.
-         */
-        final List<Integer> clone = list.clone();
-        assertEquals(copy, clone);
-        assertEquals(copy.remove(100), clone.remove(100));
-        assertEquals(copy, clone);
+        assertEquals("Comparison with reference implementation", copy, list);
+        assertEquals("hashCode()", copy.hashCode(), list.hashCode());
+        /*
+         * Test the stream, using the ArrayList as a reference implementation. This will
indirectly
+         * use the PrimitiveSpliterator.forEachRemaining(Consumer<? super Integer>)
method. A more
+         * specific test using forEachRemaining(IntConsumer) is done by the testInts() method.
+         */
+        assertStreamEquals(copy.iterator(), list.stream());
+        /*
+         * Tests cloning and removal of values in a range of indices. The IntegerList.removeRange(…)
+         * method is invoked indirectly by subList(…).clear(). Again, we use ArrayList
as a reference.
+         */
+        final IntegerList clone = list.clone();
+        assertEquals("clone()", copy, clone);
+        assertEquals("remove(int)", copy.remove(100), clone.remove(100));
+        assertEquals("remove(int)", copy, clone);
         copy .subList(128, 256).clear();
         clone.subList(128, 256).clear();
-        assertEquals(copy, clone);
+        assertEquals("After removeRange(…)", copy, clone);
+        /*
+         * Tests iterator on primitive integers, with random removal of some elements during
traversal.
+         */
+        final PrimitiveIterator.OfInt it = clone.iterator();
+        final Iterator<Integer> itRef = copy.iterator();
+        while (itRef.hasNext()) {
+            assertTrue("hasNext()", it.hasNext());
+            assertEquals(itRef.next().intValue(), it.nextInt());
+            if (random.nextInt(10) == 0) {
+                itRef.remove();
+                it.remove();
+            }
+        }
+        assertFalse("hasNext()", it.hasNext());
+        assertEquals("After remove()", copy, clone);
+        /*
+         * Verify that serialization and deserialization gives a new list with identical
content.
+         */
+        assertNotSame("Serialization", list, assertSerializedEquals(list));
     }
 
     /**
@@ -114,15 +148,14 @@ public final strictfp class IntegerListT
      * already filled with random values prior this method call.
      */
     private void testFill(final int value) {
-        assertEquals(400, list.size());
         final Set<Integer> set = new HashSet<>();
         list.fill(value);
         set.addAll(list);
-        assertEquals(Collections.singleton(value), set);
+        assertEquals("fill(value)", Collections.singleton(value), set);
         list.fill(0);
         set.clear();
         set.addAll(list);
-        assertEquals(Collections.singleton(0), set);
+        assertEquals("fill(0)", Collections.singleton(0), set);
     }
 
     /**
@@ -167,14 +200,13 @@ public final strictfp class IntegerListT
     @Test
     public void test100() {
         testReadWrite(100);
-        assertEquals(400, list.size());
         final int old100 = list.getInt(100);
         list.resize(101);
-        assertEquals(old100, list.getInt(100));
+        assertEquals("getInt(last)", old100, list.getInt(100));
         list.resize(200);
-        assertEquals(200, list.size());
-        assertEquals(old100, list.getInt(100));
-        assertEquals(0, list.getInt(101));
+        assertEquals("size()",              200, list.size());
+        assertEquals("getInt(existing)", old100, list.getInt(100));
+        assertEquals("getInt(new)",           0, list.getInt(101));
         for (int i=101; i<200; i++) {
             assertEquals(0, list.getInt(i));
         }



Mime
View raw message