Port java.util.Collection.removeIf from OpenJDK8
Source code is taken from jdk8u60.
Bug: 27538943
Change-Id: I485c024f87871e33d2f455be9807775601af77ee
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
index 0e9b161..87b1cd9 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/ArrayListTest.java
@@ -32,6 +32,8 @@
import tests.support.Support_ListTest;
import libcore.java.util.ForEachRemainingTester;
+import static libcore.java.util.RemoveIfTester.*;
+
public class ArrayListTest extends junit.framework.TestCase {
List alist;
@@ -1139,6 +1141,14 @@
}
}
+ public void test_removeIf() {
+ runBasicRemoveIfTests(ArrayList<Integer>::new);
+ runBasicRemoveIfTestsUnordered(ArrayList<Integer>::new);
+ runRemoveIfOnEmpty(ArrayList<Integer>::new);
+ testRemoveIfNPE(ArrayList<Integer>::new);
+ testRemoveIfCME(ArrayList<Integer>::new);
+ }
+
/**
* Sets up the fixture, for example, open a network connection. This method
* is called before a test is executed.
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java
index d5503f8..8b45079 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/CollectionsTest.java
@@ -1649,6 +1649,13 @@
// Correct
}
+ try {
+ c.removeIf(x -> true);
+ fail("Allowed modification of collection");
+ } catch (UnsupportedOperationException e) {
+ // Correct
+ }
+
Collection myCollection = new ArrayList();
myCollection.add(new Integer(20));
myCollection.add(null);
@@ -1704,6 +1711,13 @@
// Correct
}
+ try {
+ c.removeIf(x -> true);
+ fail("Allowed modification of list");
+ } catch (UnsupportedOperationException e) {
+ // Correct
+ }
+
// test with a Random Access List
List smallList = new ArrayList();
smallList.add(null);
@@ -1837,6 +1851,12 @@
} catch (UnsupportedOperationException e) {
// Correct
}
+ try {
+ c.removeIf(x -> true);
+ fail("Allowed modification of set");
+ } catch (UnsupportedOperationException e) {
+ // Correct
+ }
Set mySet = Collections.unmodifiableSet(new HashSet());
assertTrue("Should not contain null", !mySet.contains(null));
@@ -1897,20 +1917,18 @@
assertTrue("Returned set missing elements", c.contains(i.next()));
try {
c.add(new Object());
- } catch (UnsupportedOperationException e) {
- exception = true;
- // Correct
- }
- if (!exception) {
fail("Allowed modification of set");
- }
+ } catch (UnsupportedOperationException e) {}
+
try {
c.remove(new Object());
- } catch (UnsupportedOperationException e) {
- // Correct
- return;
- }
- fail("Allowed modification of set");
+ fail("Allowed modification of set");
+ } catch (UnsupportedOperationException expected) {}
+
+ try {
+ c.removeIf(x -> true);
+ fail("Allowed modification of set");
+ } catch (UnsupportedOperationException expected) {}
}
/**
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java
index 1904aa9..da01e34 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedListTest.java
@@ -28,7 +28,6 @@
import java.util.ListIterator;
import java.util.NoSuchElementException;
import java.util.Spliterator;
-import java.util.Vector;
import libcore.java.util.ForEachRemainingTester;
import libcore.java.util.SpliteratorTester;
@@ -37,6 +36,8 @@
import tests.support.Support_ListTest;
+import static libcore.java.util.RemoveIfTester.*;
+
public class LinkedListTest extends junit.framework.TestCase {
LinkedList ll;
@@ -982,6 +983,14 @@
}
}
+ public void test_removeIf() {
+ runBasicRemoveIfTests(LinkedList<Integer>::new);
+ runBasicRemoveIfTestsUnordered(LinkedList<Integer>::new);
+ runRemoveIfOnEmpty(LinkedList<Integer>::new);
+ testRemoveIfNPE(LinkedList<Integer>::new);
+ testRemoveIfCME(LinkedList<Integer>::new);
+ }
+
/**
* java.util.LinkedList#Serialization()
*/
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
index 0b56eec..a6f5626 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
@@ -27,12 +27,15 @@
import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.HashSet;
+import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Spliterator;
import java.util.Vector;
+import static libcore.java.util.RemoveIfTester.*;
+
public class VectorTest extends junit.framework.TestCase {
private Vector tVector = new Vector();
@@ -1464,6 +1467,14 @@
}
}
+ public void test_removeIf() {
+ runBasicRemoveIfTests(Vector<Integer>::new);
+ runBasicRemoveIfTestsUnordered(Vector<Integer>::new);
+ runRemoveIfOnEmpty(Vector<Integer>::new);
+ testRemoveIfNPE(Vector<Integer>::new);
+ testRemoveIfCME(Vector<Integer>::new);
+ }
+
/**
* Sets up the fixture, for example, open a network connection. This method
* is called before a test is executed.
diff --git a/luni/src/test/java/libcore/java/util/RemoveIfTester.java b/luni/src/test/java/libcore/java/util/RemoveIfTester.java
new file mode 100644
index 0000000..f0fdd42
--- /dev/null
+++ b/luni/src/test/java/libcore/java/util/RemoveIfTester.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * 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 libcore.java.util;
+
+import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+
+import static junit.framework.Assert.assertTrue;
+import static junit.framework.Assert.fail;
+
+/**
+ * Tests behavior common to all implementations of {@link Collection#removeIf(Predicate)}.
+ */
+public class RemoveIfTester {
+
+ private static final Predicate<Integer> isEven = x -> x % 2 == 0;
+ private static final Predicate<Integer> isOdd = isEven.negate();
+
+ public static void runBasicRemoveIfTests(Supplier<Collection<Integer>> supp) {
+ Collection<Integer> integers = supp.get();
+ for (int h = 0; h < 100; ++h) {
+ // Insert some ordered integers.
+ integers.add(h);
+ }
+
+ integers.removeIf(isEven);
+ Integer prev = null;
+ for (Integer i : integers) {
+ assertTrue(i % 2 != 0);
+ if (prev != null) {
+ assertTrue(prev <= i);
+ }
+ prev = i;
+ }
+
+ integers.removeIf(isOdd);
+ assertTrue(integers.isEmpty());
+ }
+
+ public static void runBasicRemoveIfTestsUnordered(Supplier<Collection<Integer>> supp) {
+ Collection<Integer> integers = supp.get();
+ for (int h = 0; h < 100; ++h) {
+ // Insert a bunch of arbitrary integers.
+ integers.add((h >>> 2) ^ (h >>> 5) ^ (h >>> 11) ^ (h >>> 17));
+ }
+
+ integers.removeIf(isEven);
+ for (Integer i : integers) {
+ assertTrue(i % 2 != 0);
+ }
+
+ integers.removeIf(isOdd);
+ assertTrue(integers.isEmpty());
+ }
+
+ /**
+ * Removing from an empty collection should not fail.
+ */
+ public static void runRemoveIfOnEmpty(Supplier<Collection<Integer>> supp) {
+ supp.get().removeIf(x -> {
+ fail();
+ return false;
+ });
+ }
+
+ public static void testRemoveIfNPE(Supplier<Collection<Integer>> supp) {
+ try {
+ supp.get().removeIf(null);
+ fail();
+ } catch (NullPointerException expected) {}
+ }
+
+ public static void testRemoveIfCME(Supplier<Collection<Integer>> supp) {
+ Collection<Integer> c = supp.get();
+ c.add(0);
+
+ try {
+ c.removeIf(x -> {c.add(42); return true;});
+ fail();
+ } catch (ConcurrentModificationException expected) {
+ }
+ }
+}
diff --git a/ojluni/src/main/java/java/util/ArrayList.java b/ojluni/src/main/java/java/util/ArrayList.java
index c6ecd33..fec492c 100755
--- a/ojluni/src/main/java/java/util/ArrayList.java
+++ b/ojluni/src/main/java/java/util/ArrayList.java
@@ -27,6 +27,7 @@
package java.util;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* Resizable-array implementation of the <tt>List</tt> interface. Implements
@@ -1366,4 +1367,47 @@
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
+
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ Objects.requireNonNull(filter);
+ // figure out which elements are to be removed
+ // any exception thrown from the filter predicate at this stage
+ // will leave the collection unmodified
+ int removeCount = 0;
+ final BitSet removeSet = new BitSet(size);
+ final int expectedModCount = modCount;
+ final int size = this.size;
+ for (int i=0; modCount == expectedModCount && i < size; i++) {
+ @SuppressWarnings("unchecked")
+ final E element = (E) elementData[i];
+ if (filter.test(element)) {
+ removeSet.set(i);
+ removeCount++;
+ }
+ }
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+
+ // shift surviving elements left over the spaces left by removed elements
+ final boolean anyToRemove = removeCount > 0;
+ if (anyToRemove) {
+ final int newSize = size - removeCount;
+ for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
+ i = removeSet.nextClearBit(i);
+ elementData[j] = elementData[i];
+ }
+ for (int k=newSize; k < size; k++) {
+ elementData[k] = null; // Let gc do its work
+ }
+ this.size = newSize;
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ modCount++;
+ }
+
+ return anyToRemove;
+ }
}
diff --git a/ojluni/src/main/java/java/util/Collection.java b/ojluni/src/main/java/java/util/Collection.java
index 72ee9ae..9f0227f 100755
--- a/ojluni/src/main/java/java/util/Collection.java
+++ b/ojluni/src/main/java/java/util/Collection.java
@@ -25,6 +25,8 @@
package java.util;
+import java.util.function.Predicate;
+
/**
* The root interface in the <i>collection hierarchy</i>. A collection
* represents a group of objects, known as its <i>elements</i>. Some
@@ -366,6 +368,42 @@
*/
boolean removeAll(Collection<?> c);
+
+ /**
+ * Removes all of the elements of this collection that satisfy the given
+ * predicate. Errors or runtime exceptions thrown during iteration or by
+ * the predicate are relayed to the caller.
+ *
+ * @implSpec
+ * The default implementation traverses all elements of the collection using
+ * its {@link #iterator}. Each matching element is removed using
+ * {@link Iterator#remove()}. If the collection's iterator does not
+ * support removal then an {@code UnsupportedOperationException} will be
+ * thrown on the first matching element.
+ *
+ * @param filter a predicate which returns {@code true} for elements to be
+ * removed
+ * @return {@code true} if any elements were removed
+ * @throws NullPointerException if the specified filter is null
+ * @throws UnsupportedOperationException if elements cannot be removed
+ * from this collection. Implementations may throw this exception if a
+ * matching element cannot be removed or if, in general, removal is not
+ * supported.
+ * @since 1.8
+ */
+ default boolean removeIf(Predicate<? super E> filter) {
+ Objects.requireNonNull(filter);
+ boolean removed = false;
+ final Iterator<E> each = iterator();
+ while (each.hasNext()) {
+ if (filter.test(each.next())) {
+ each.remove();
+ removed = true;
+ }
+ }
+ return removed;
+ }
+
/**
* Retains only the elements in this collection that are contained in the
* specified collection (optional operation). In other words, removes from
diff --git a/ojluni/src/main/java/java/util/Collections.java b/ojluni/src/main/java/java/util/Collections.java
index 8baf0e7..9912283 100755
--- a/ojluni/src/main/java/java/util/Collections.java
+++ b/ojluni/src/main/java/java/util/Collections.java
@@ -33,7 +33,7 @@
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
-
+import java.util.function.Predicate;
/**
* This class consists exclusively of static methods that operate on or return
@@ -1146,6 +1146,10 @@
public void forEach(Consumer<? super E> action) {
c.forEach(action);
}
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ throw new UnsupportedOperationException();
+ }
@SuppressWarnings("unchecked")
@Override
public Spliterator<E> spliterator() {
@@ -1813,6 +1817,10 @@
synchronized (mutex) {c.forEach(consumer);}
}
@Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ synchronized (mutex) {return c.removeIf(filter);}
+ }
+ @Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
@@ -2536,6 +2544,10 @@
@Override
public void forEach(Consumer<? super E> action) {c.forEach(action);}
@Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ return c.removeIf(filter);
+ }
+ @Override
public Spliterator<E> spliterator() {return c.spliterator();}
}
@@ -3356,6 +3368,11 @@
Objects.requireNonNull(action);
}
@Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ Objects.requireNonNull(filter);
+ return false;
+ }
+ @Override
public Spliterator<E> spliterator() { return Spliterators.emptySpliterator(); }
// Preserves singleton property
@@ -3435,6 +3452,12 @@
public int hashCode() { return 1; }
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ Objects.requireNonNull(filter);
+ return false;
+ }
+
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {
@@ -3584,6 +3607,10 @@
public void forEach(Consumer<? super E> action) {
action.accept(element);
}
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ throw new UnsupportedOperationException();
+ }
}
/**
@@ -3630,6 +3657,10 @@
public void forEach(Consumer<? super E> action) {
action.accept(element);
}
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ throw new UnsupportedOperationException();
+ }
}
/**
@@ -4190,6 +4221,10 @@
public void forEach(Consumer<? super E> action) {
s.forEach(action);
}
+ @Override
+ public boolean removeIf(Predicate<? super E> filter) {
+ return s.removeIf(filter);
+ }
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException
@@ -4251,5 +4286,8 @@
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> action) {q.forEach(action);}
+ public boolean removeIf(Predicate<? super E> filter) {
+ return q.removeIf(filter);
+ }
}
}
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index f43326f..6afdfbd 100755
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -26,6 +26,7 @@
package java.util;
import java.util.function.Consumer;
+import java.util.function.Predicate;
/**
* The {@code Vector} class implements a growable array of
@@ -1355,4 +1356,48 @@
return Spliterator.ORDERED | Spliterator.SIZED | Spliterator.SUBSIZED;
}
}
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public synchronized boolean removeIf(Predicate<? super E> filter) {
+ Objects.requireNonNull(filter);
+ // figure out which elements are to be removed
+ // any exception thrown from the filter predicate at this stage
+ // will leave the collection unmodified
+ int removeCount = 0;
+ final int size = elementCount;
+ final BitSet removeSet = new BitSet(size);
+ final int expectedModCount = modCount;
+ for (int i=0; modCount == expectedModCount && i < size; i++) {
+ @SuppressWarnings("unchecked")
+ final E element = (E) elementData[i];
+ if (filter.test(element)) {
+ removeSet.set(i);
+ removeCount++;
+ }
+ }
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+
+ // shift surviving elements left over the spaces left by removed elements
+ final boolean anyToRemove = removeCount > 0;
+ if (anyToRemove) {
+ final int newSize = size - removeCount;
+ for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
+ i = removeSet.nextClearBit(i);
+ elementData[j] = elementData[i];
+ }
+ for (int k=newSize; k < size; k++) {
+ elementData[k] = null; // Let gc do its work
+ }
+ elementCount = newSize;
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ modCount++;
+ }
+
+ return anyToRemove;
+ }
}