sis-commits mailing list archives

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

URL: http://svn.apache.org/viewvc?rev=1812815&view=rev
Log:
Alexis Manin's patch: add IntStream support to IntegerList.
https://issues.apache.org/jira/browse/SIS-367

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/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=1812815&r1=1812814&r2=1812815&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 10:25:22 2017
@@ -20,12 +20,19 @@ import java.util.Arrays;
 import java.util.AbstractList;
 import java.util.RandomAccess;
 import java.util.NoSuchElementException;
+import java.util.ConcurrentModificationException;
 import java.io.IOException;
 import java.io.Serializable;
 import java.io.ObjectOutputStream;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.ArgumentChecks;
 
+// Branch-dependent imports
+import java.util.Spliterator;
+import java.util.function.IntConsumer;
+import java.util.stream.IntStream;
+import java.util.stream.StreamSupport;
+
 
 /**
  * A list of unsigned integer values. This class packs the values in the minimal amount of
bits
@@ -34,7 +41,8 @@ import org.apache.sis.util.ArgumentCheck
  * <p>This class is <strong>not</strong> thread-safe. Synchronizations
(if wanted) are user's responsibility.</p>
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @author  Alexis Manin (Geomatys)
+ * @version 0.8
  *
  * @see org.apache.sis.math.Vector
  *
@@ -157,6 +165,7 @@ public class IntegerList extends Abstrac
      */
     public void resize(final int size) {
         ArgumentChecks.ensurePositive("size", size);
+        modCount++;
         if (size > this.size) {
             int base = this.size * bitCount;
             final int offset = base & OFFSET_MASK;
@@ -183,6 +192,7 @@ public class IntegerList extends Abstrac
     @SuppressWarnings("fallthrough")
     public void fill(int value) {
         ArgumentChecks.ensureBetween("value", 0, mask, value);
+        modCount++;
         final long p;
         if (value == 0) {
             p = 0;                              // All bits set to 0.
@@ -210,6 +220,7 @@ public class IntegerList extends Abstrac
      */
     @Override
     public void clear() {
+        modCount++;
         size = 0;
     }
 
@@ -237,6 +248,7 @@ public class IntegerList extends Abstrac
      */
     public void addInt(final int value) throws IllegalArgumentException {
         ArgumentChecks.ensureBetween("value", 0, mask, value);
+        modCount++;
         final int last = size;
         final int length = length(++size);
         if (length > values.length) {
@@ -318,6 +330,7 @@ public class IntegerList extends Abstrac
     public void setInt(int index, int value) throws IndexOutOfBoundsException {
         ArgumentChecks.ensureValidIndex(size, index);
         ArgumentChecks.ensureBetween("value", 0, mask, value);
+        modCount++;
         setUnchecked(index, value);
     }
 
@@ -352,6 +365,7 @@ public class IntegerList extends Abstrac
     @Override
     public Integer remove(final int index) throws IndexOutOfBoundsException {
         final Integer old = get(index);
+        modCount++;
         removeRange(index, index+1);
         return old;
     }
@@ -364,6 +378,7 @@ public class IntegerList extends Abstrac
      */
     public int removeLast() throws NoSuchElementException {
         if (size != 0) {
+            modCount++;
             return getUnchecked(--size);
         }
         throw new NoSuchElementException();
@@ -443,6 +458,7 @@ public class IntegerList extends Abstrac
             throw new AssertionError(e);
         }
         clone.values = clone.values.clone();
+        clone.modCount = 0;
         return clone;
     }
 
@@ -456,4 +472,94 @@ public class IntegerList extends Abstrac
         trimToSize();
         out.defaultWriteObject();
     }
+
+    /**
+     * Returns a sequential stream of integers with this {@code IntegerList} as its source.
+     * This method is similar to {@link #stream()}, but does not box the values.
+     * 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.
+     *
+     * @return a stream of values in this list as primitive types.
+     *
+     * @since 0.8
+     */
+    public IntStream ints() {
+        return StreamSupport.intStream(new PrimitiveSpliterator(), false);
+    }
+
+    /**
+     * Same as {@link #spliterator()}, but without value boxing.
+     * 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}.
+     */
+    private final class PrimitiveSpliterator implements Spliterator.OfInt {
+        /**
+         * Index of the next element to be returned.
+         */
+        private int nextIdx;
+
+        /**
+         * The {@link IntegerList#modCount} value as iterator construction time.
+         * Used for detecting modification in the backing list during traversal.
+         */
+        private final int expectedModCount = modCount;
+
+        /**
+         * Creates a new iterator for the whole content of the backing list.
+         */
+        PrimitiveSpliterator() {
+        }
+
+        /**
+         * Declares that this split iterator does not return null elements, that all elements
are
+         * traversed in a fixed order (which is increasing index values) and that {@link
#size()}
+         * represents an exact count of elements.
+         */
+        @Override
+        public int characteristics() {
+            return NONNULL | ORDERED | SIZED;
+        }
+
+        /**
+         * Returns the number of values in the backing list.
+         */
+        @Override
+        public long estimateSize() {
+            return size();
+        }
+
+        /**
+         * @todo for now, we keep it simple and forbid parallelism. In the future,
+         *       we could use an approach as the one in java standard array lists.
+         */
+        @Override
+        public OfInt trySplit() {
+            return null;
+        }
+
+        /**
+         * 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();
+            if (canAdvance) {
+                action.accept(getUnchecked(nextIdx++));
+            }
+            return canAdvance;
+        }
+
+        /**
+         * Ensures that no alteration has happened on the backing list since the
+         * spliterator creation.
+         */
+        private void checkForComodification() {
+            if (modCount != expectedModCount) {
+                throw new ConcurrentModificationException();
+            }
+        }
+    }
 }

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=1812815&r1=1812814&r2=1812815&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 10:25:22 2017
@@ -22,6 +22,7 @@ import java.util.Set;
 import java.util.HashSet;
 import java.util.Collections;
 import java.util.Random;
+import java.util.ConcurrentModificationException;
 import org.apache.sis.test.TestCase;
 import org.apache.sis.test.TestUtilities;
 import org.junit.Test;
@@ -29,12 +30,18 @@ import org.junit.Test;
 import static java.lang.StrictMath.*;
 import static org.apache.sis.test.Assert.*;
 
+// Branch-dependent imports
+import java.util.function.IntConsumer;
+import java.util.PrimitiveIterator;
+import java.util.stream.IntStream;
+
 
 /**
  * Tests {@link IntegerList} implementations.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 0.7
+ * @author  Alexis Manin (Geomatys)
+ * @version 0.8
  * @since   0.7
  * @module
  */
@@ -192,4 +199,58 @@ public final strictfp class IntegerListT
         testReadWrite(Integer.MAX_VALUE);
         testFill(17);
     }
+
+    /**
+     * Tests that primitive stream traversal is coherent with its list value.
+     */
+    @Test
+    public void testInts() {
+        list = createRandomlyFilled(42, 404);
+        list.ints().forEach(new IntConsumer() {
+            private int index = 0;
+
+            @Override
+            public void accept(int value) {
+                assertEquals("Spliterator value differs from its original list", list.getInt(index++),
value);
+            }
+        });
+    }
+
+    /**
+     * Ensures our stream is a fail-fast operator, i.e: it fails when the list has
+     * been modified before the end of its iteration.
+     */
+    @Test
+    public void testErrorOnCoModification() {
+        list = createRandomlyFilled(4, 10);
+        final PrimitiveIterator.OfInt values = list.ints().iterator();
+
+        // Start iteration normally.
+        assertEquals(list.getInt(0), values.nextInt());
+        assertEquals(list.getInt(1), values.nextInt());
+
+        // Now, if we alter the list and then try to use previously created stream, we should
get an error.
+        list.add(0);
+        try {
+            values.next();
+            fail("Concurrent modification has not been detected.");
+        } catch (ConcurrentModificationException expected) {
+            // Expected behavior
+        }
+    }
+
+    /**
+     * Creates a new list whose capacity and value magnitude are defined as input.
+     * The list is filled by a random integer generator before return.
+     *
+     * @param  size      number of elements to insert in the list.
+     * @param  maxValue  maximum value to use for value insertion.
+     * @return a fresh and filled list.
+     */
+    private static IntegerList createRandomlyFilled(final int size, final int maxValue) {
+        final Random random = TestUtilities.createRandomNumberGenerator();
+        return IntStream.generate(() -> random.nextInt(maxValue))
+                .limit(size)
+                .collect(() -> new IntegerList(size, maxValue), IntegerList::addInt, null);
+    }
 }



Mime
View raw message