Add Iterable#forEach & Map#forEach from openJdk8
Based on openJdk 8u40 source & iam@ stream change in
ag/872080
Bug: 27404545
Change-Id: Ic67e20b35c24e7acce513e010b727510af09a83e
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 d33f5f2..90feb5f 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
@@ -1047,6 +1047,60 @@
assertEquals("string2", it.next());
}
+ public void test_forEach() throws Exception {
+ ArrayList<Integer> list = new ArrayList<>();
+ list.add(0);
+ list.add(1);
+ list.add(2);
+
+ ArrayList<Integer> output = new ArrayList<>();
+ list.forEach(k -> output.add(k));
+
+ assertEquals(list, output);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ ArrayList<Integer> list = new ArrayList<>();
+ try {
+ list.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ ArrayList<Integer> list = new ArrayList<>();
+ list.add(1);
+ list.add(2);
+ ArrayList<Integer> processed = new ArrayList<>();
+ try {
+ list.forEach(new java.util.function.Consumer<Integer>() {
+ @Override
+ public void accept(Integer t) {
+ processed.add(t);
+ list.add(t);}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ assertEquals(1, processed.size());
+ }
+
+ public void test_forEach_CME_onLastElement() throws Exception {
+ ArrayList<Integer> list = new ArrayList<>();
+ list.add(1);
+ list.add(2);
+ list.add(3);
+ try {
+ list.forEach(new java.util.function.Consumer<Integer>() {
+ @Override
+ public void accept(Integer t) {
+ if (t == 3) {
+ list.add(t);
+ }
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ }
/**
* Sets up the fixture, for example, open a network connection. This method
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Arrays2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Arrays2Test.java
index d6adbb5..a61e343 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Arrays2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Arrays2Test.java
@@ -5,9 +5,9 @@
* The ASF licenses this file to You 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
@@ -19,7 +19,9 @@
import java.io.Serializable;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Comparator;
+import java.util.ConcurrentModificationException;
import java.util.List;
import java.util.RandomAccess;
@@ -469,4 +471,19 @@
} catch (NullPointerException e) {
}
}
+
+ public void test_forEach() throws Exception {
+ List<Integer> list = Arrays.asList(0, 1, 2);
+ ArrayList<Integer> output = new ArrayList<>();
+ list.forEach(k -> output.add(k));
+ assertEquals(list, output);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ List<Integer> list = Arrays.asList(0, 1, 2);
+ try {
+ list.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Collections2Test.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Collections2Test.java
index 899cd18..e5cce1c 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Collections2Test.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/Collections2Test.java
@@ -5,9 +5,9 @@
* The ASF licenses this file to You 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
@@ -22,16 +22,19 @@
import org.apache.harmony.testframework.serialization.SerializationTest.SerializableAssert;
import tests.util.SerializationTester;
import java.io.Serializable;
+import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
+import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
+import java.util.Queue;
import java.util.RandomAccess;
import java.util.Set;
import java.util.SortedMap;
@@ -492,4 +495,95 @@
}
}
+ void testCollectionForEach(Collection<Integer> collection) {
+ ArrayList<Integer> output = new ArrayList<Integer>();
+ collection.forEach(k -> output.add(k));
+
+ assertEquals(new ArrayList<>(collection), output);
+ }
+
+ public void test_Collection_forEach() {
+ ArrayList<Integer> list = new ArrayList<Integer>();
+ list.add(0);
+ list.add(1);
+ list.add(2);
+ testCollectionForEach(Collections.unmodifiableCollection(list));
+ testCollectionForEach(Collections.synchronizedCollection(list));
+ testCollectionForEach(Collections.checkedCollection(list, Integer.class));
+ testCollectionForEach(Collections.singletonList(new Integer(0)));
+ }
+
+ void testMapForEach(Map<String,String> map) {
+ HashMap<String, String> output = new HashMap<String, String>();
+ map.forEach((k, v) -> output.put(k, v));
+ assertEquals(map, output);
+
+ output.clear();
+ map.entrySet().forEach(entry -> output.put(entry.getKey(), entry.getValue()));
+ assertEquals(map, output);
+
+ HashSet<String> setOutput = new HashSet<>();
+ map.values().forEach(value -> setOutput.add(value));
+ assertEquals(new HashSet<>(map.values()), setOutput);
+
+ setOutput.clear();
+ map.keySet().forEach((k) -> setOutput.add(k));
+ assertEquals(map.keySet(), setOutput);
+ }
+
+ public void test_Map_forEach() {
+ HashMap<String, String> map = new HashMap<String, String>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+ testMapForEach(Collections.unmodifiableMap(map));
+ testMapForEach(Collections.synchronizedMap(map));
+ testMapForEach(Collections.checkedMap(map, String.class, String.class));
+ testMapForEach(Collections.singletonMap("one", "1"));
+ }
+
+ void testSetForEach(Set<Integer> set) {
+ HashSet<Integer> output = new HashSet<Integer>();
+ set.forEach(k -> output.add(k));
+
+ assertEquals(set.size(), output.size());
+ for (Integer key : set) {
+ assertTrue(output.contains(key));
+ }
+ }
+
+ public void test_Set_forEach() {
+ HashSet<Integer> set = new HashSet<Integer>();
+ set.add(1);
+ set.add(2);
+ set.add(3);
+ testSetForEach(Collections.unmodifiableSet(set));
+ testSetForEach(Collections.synchronizedSet(set));
+ testSetForEach(Collections.checkedSet(set, Integer.class));
+ testSetForEach(Collections.singleton(1));
+
+ Set<Integer> fromMap = Collections.newSetFromMap(new HashMap<Integer, Boolean>());
+ fromMap.add(1);
+ fromMap.add(2);
+ fromMap.add(3);
+ testSetForEach(fromMap);
+ }
+
+
+ public void test_Queue_forEach() {
+ Deque<Integer> deque = new ArrayDeque<Integer>();
+ deque.addFirst(2);
+ deque.addFirst(1);
+ deque.addFirst(0);
+
+ Queue<Integer> queue = Collections.asLifoQueue(deque);
+ ArrayList<Integer> output = new ArrayList<Integer>();
+ queue.forEach(v -> output.add(v));
+
+ assertEquals(3, output.size());
+ assertEquals(0, (int)output.get(0));
+ assertEquals(1, (int)output.get(1));
+ assertEquals(2, (int)output.get(2));
+ }
+
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java
index 26d0a8e..eb2bb47 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashMapTest.java
@@ -27,7 +27,9 @@
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Random;
@@ -689,6 +691,115 @@
assertFalse(entrySet.contains(copyEntry));
}
+ public void test_forEach() throws Exception {
+ HashMap<String, String> map = new HashMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ HashMap<String, String> output = new HashMap<>();
+ map.forEach((k, v) -> output.put(k,v));
+ assertEquals(map, output);
+
+ HashSet<String> setOutput = new HashSet<>();
+ map.keySet().forEach((k) -> setOutput.add(k));
+ assertEquals(map.keySet(), setOutput);
+
+ setOutput.clear();
+ map.values().forEach((v) -> setOutput.add(v));
+ assertEquals(new HashSet<>(map.values()), setOutput);
+
+ HashSet<Map.Entry<String,String>> entrySetOutput = new HashSet<>();
+ map.entrySet().forEach((v) -> entrySetOutput.add(v));
+ assertEquals(map.entrySet(), entrySetOutput);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ HashMap<String, String> map = new HashMap<>();
+ try {
+ map.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.keySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.values().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.entrySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ HashMap<String, String> map = new HashMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ HashMap<String, String> outputMap = new HashMap<>();
+ try {
+ map.forEach(new java.util.function.BiConsumer<String, String>() {
+ @Override
+ public void accept(String k, String v) {
+ outputMap.put(k, v);
+ map.put("foo1", v);
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.keySet().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {
+ outputMap.put(k, "foo");
+ map.put("foo2", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.values().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {
+ outputMap.put(k, "foo");
+ map.put("foo3", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.entrySet().forEach(new java.util.function.Consumer<Map.Entry<String,String>>() {
+ @Override
+ public void accept(Map.Entry<String,String> k) {
+ outputMap.put(k.getKey(), "foo");
+ map.put("foo4", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+ }
+
private static class MockEntry implements Map.Entry {
public Object getKey() {
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java
index 4ff4b9d..7add356 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashSetTest.java
@@ -18,6 +18,7 @@
package org.apache.harmony.tests.java.util;
import java.util.Arrays;
+import java.util.ConcurrentModificationException;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@@ -222,6 +223,39 @@
cloned.add(new Integer(8));
}
+ public void test_forEach() throws Exception {
+ HashSet<Integer> hs = new HashSet<>();
+ hs.add(0);
+ hs.add(1);
+ hs.add(2);
+
+ HashSet<Integer> output = new HashSet<>();
+ hs.forEach(k -> output.add(k));
+
+ assertEquals(hs, output);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ HashSet<String> set = new HashSet<>();
+ try {
+ set.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ HashSet<String> set = new HashSet<>();
+ set.add("one");
+ set.add("two");
+ try {
+ set.forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {set.add("foo");}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ }
+
/**
* 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/HashtableTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java
index 2c81f65..165ff23 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/HashtableTest.java
@@ -895,6 +895,46 @@
}
}
+ public void test_forEach() throws Exception {
+ Hashtable<String, String> ht = new Hashtable<>();
+ ht.put("1", "one");
+ ht.put("2", "two");
+ ht.put("3", "three");
+ Hashtable<String, String> output = new Hashtable<>();
+
+ ht.forEach((k,v) -> output.put(k,v));
+ assertEquals(ht, output);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ Hashtable<String, String> ht = new Hashtable<>();
+ try {
+ ht.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ Hashtable<String, String> ht = new Hashtable<>();
+ ht.put("one", "1");
+ ht.put("two", "2");
+ ht.put("three", "3");
+
+ Hashtable<String, String> outputHt = new Hashtable<>();
+ try {
+ ht.forEach(new java.util.function.BiConsumer<String, String>() {
+ @Override
+ public void accept(String k, String v) {
+ outputHt.put(k, v);
+ ht.put("foo", v);
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputHt.size());
+ }
+
protected Hashtable hashtableClone(Hashtable s) {
return (Hashtable) s.clone();
}
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
index 4ddaf65..bb85ec6 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/IdentityHashMapTest.java
@@ -25,6 +25,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
@@ -857,6 +858,116 @@
assertSame(newValue, ihm.get(key));
}
+ public void test_forEach() throws Exception {
+ IdentityHashMap<String, String> map = new IdentityHashMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ IdentityHashMap<String, String> output = new IdentityHashMap<>();
+ map.forEach((k, v) -> output.put(k,v));
+ assertEquals(map, output);
+
+ HashSet<String> setOutput = new HashSet<>();
+ map.keySet().forEach((k) -> setOutput.add(k));
+ assertEquals(map.keySet(), setOutput);
+
+ setOutput.clear();
+ map.values().forEach((v) -> setOutput.add(v));
+ assertEquals(new HashSet<>(map.values()), setOutput);
+
+ HashSet<Map.Entry<String,String>> entrySetOutput = new HashSet<>();
+ map.entrySet().forEach((v) -> entrySetOutput.add(v));
+ assertEquals(map.entrySet(), entrySetOutput);
+ }
+
+
+ public void test_forEach_NPE() throws Exception {
+ IdentityHashMap<String, String> map = new IdentityHashMap<>();
+ try {
+ map.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.keySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.values().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.entrySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ IdentityHashMap<String, String> map = new IdentityHashMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ IdentityHashMap<String, String> outputMap = new IdentityHashMap<>();
+ try {
+ map.forEach(new java.util.function.BiConsumer<String, String>() {
+ @Override
+ public void accept(String k, String v) {
+ outputMap.put(k, v);
+ map.put("foo1", v);
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.keySet().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {
+ outputMap.put(k, "foo");
+ map.put("foo2", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.values().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {
+ outputMap.put(k, "foo");
+ map.put("foo3", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.entrySet().forEach(new java.util.function.Consumer<Map.Entry<String,String>>() {
+ @Override
+ public void accept(Map.Entry<String,String> k) {
+ outputMap.put(k.getKey(), "foo");
+ map.put("foo4", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+ }
+
// comparator for IdentityHashMap objects
private static final SerializableAssert COMPARATOR = new SerializableAssert() {
public void assertDeserialized(Serializable initial,
diff --git a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java
index 44d2183..05873f6 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/LinkedHashMapTest.java
@@ -21,8 +21,10 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
@@ -705,6 +707,116 @@
assertTrue("Entries left in map", !it1.hasNext());
}
+ public void test_forEach() throws Exception {
+ LinkedHashMap<String, String> map = new LinkedHashMap<>();
+ map.put("three", "3");
+ map.put("two", "2");
+ map.put("one", "1");
+
+ LinkedHashMap<String, String> output = new LinkedHashMap<>();
+ map.forEach((k, v) -> output.put(k,v));
+ assertEquals(map, output);
+
+ LinkedHashSet<String> setOutput = new LinkedHashSet<>();
+ map.keySet().forEach((k) -> setOutput.add(k));
+ assertEquals(map.keySet(), setOutput);
+
+ setOutput.clear();
+ map.values().forEach((v) -> setOutput.add(v));
+ assertEquals(new LinkedHashSet<>(map.values()), setOutput);
+
+ LinkedHashSet<Map.Entry<String,String>> entrySetOutput = new LinkedHashSet<>();
+ map.entrySet().forEach((v) -> entrySetOutput.add(v));
+ assertEquals(map.entrySet(), entrySetOutput);
+ }
+
+
+ public void test_forEach_NPE() throws Exception {
+ LinkedHashMap<String, String> map = new LinkedHashMap<>();
+ try {
+ map.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.keySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.values().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.entrySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ LinkedHashMap<String, String> map = new LinkedHashMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ LinkedHashMap<String, String> outputMap = new LinkedHashMap<>();
+ try {
+ map.forEach(new java.util.function.BiConsumer<String, String>() {
+ @Override
+ public void accept(String k, String v) {
+ outputMap.put(k, v);
+ map.put("foo1", v);
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.keySet().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {
+ outputMap.put(k, "foo");
+ map.put("foo2", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.values().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {
+ outputMap.put(k, "foo");
+ map.put("foo3", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+
+ outputMap.clear();
+ try {
+ map.entrySet().forEach(new java.util.function.Consumer<Map.Entry<String,String>>() {
+ @Override
+ public void accept(Map.Entry<String,String> k) {
+ outputMap.put(k.getKey(), "foo");
+ map.put("foo4", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, outputMap.size());
+ }
+
/**
* 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/TreeMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java
index 2f11d58..14506fb 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/TreeMapTest.java
@@ -25,8 +25,10 @@
import java.text.Collator;
import java.util.AbstractMap;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
@@ -1955,6 +1957,89 @@
Iterator iter = subMap.values().iterator();
}
+ public void test_forEach() throws Exception {
+ TreeMap<String, String> map = new TreeMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ map.put("three", "3");
+
+ TreeMap<String, String> output = new TreeMap<>();
+ map.forEach((k, v) -> output.put(k,v));
+ assertEquals(map, output);
+
+ HashSet<String> setOutput = new HashSet<>();
+ map.keySet().forEach((k) -> setOutput.add(k));
+ assertEquals(map.keySet(), setOutput);
+
+ setOutput.clear();
+ map.values().forEach((v) -> setOutput.add(v));
+ assertEquals(new HashSet<>(map.values()), setOutput);
+
+ HashSet<Map.Entry<String,String>> entrySetOutput = new HashSet<>();
+ map.entrySet().forEach((v) -> entrySetOutput.add(v));
+ assertEquals(map.entrySet(), entrySetOutput);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ TreeMap<String, String> map = new TreeMap<>();
+ try {
+ map.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.keySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.values().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.entrySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ TreeMap<String, String> map = new TreeMap<>();
+ map.put("one", "1");
+ map.put("two", "2");
+ try {
+ map.forEach(new java.util.function.BiConsumer<String, String>() {
+ @Override
+ public void accept(String k, String v) {map.put("foo", v);}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+
+ try {
+ map.keySet().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {map.put("foo2", "boo");}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+
+ try {
+ map.values().forEach(new java.util.function.Consumer<String>() {
+ @Override
+ public void accept(String k) {map.put("foo3", "boo");}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+
+ try {
+ map.entrySet().forEach(new java.util.function.Consumer<Map.Entry<String,String>>() {
+ @Override
+ public void accept(Map.Entry<String,String> k) {map.put("foo4", "boo");}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ }
+
/**
* 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/VectorTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/VectorTest.java
index e32ca94..1818033 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
@@ -20,6 +20,7 @@
import tests.support.Support_ListTest;
import java.util.Arrays;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.LinkedList;
@@ -1383,6 +1384,41 @@
}
}
+ public void test_forEach() throws Exception {
+ Vector<Integer> vector = new Vector<Integer>();
+ vector.add(0);
+ vector.add(1);
+ vector.add(2);
+
+ Vector<Integer> output = new Vector<Integer>();
+ vector.forEach ( k -> output.add(k) );
+
+ assertEquals(vector, output);
+ }
+
+
+ public void test_forEach_NPE() throws Exception {
+ Vector<Integer> vector = new Vector<>();
+ try {
+ vector.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void test_forEach_CME() throws Exception {
+ Vector<Integer> vector = new Vector<>();
+ vector.add(1);
+ vector.add(2);
+ try {
+ vector.forEach(new java.util.function.Consumer<Integer>() {
+ @Override
+ public void accept(Integer t) {vector.add(t);}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ }
+
+
/**
* 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/WeakHashMapTest.java b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java
index 395f495..1732dd3 100644
--- a/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java
+++ b/harmony-tests/src/test/java/org/apache/harmony/tests/java/util/WeakHashMapTest.java
@@ -19,7 +19,10 @@
import java.util.AbstractMap;
import java.util.Arrays;
+import java.util.ArrayList;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -395,6 +398,113 @@
assertEquals("Incorrect number of keys returned after gc,", 99, valuesCollection.size());
}
+ public void test_forEach() throws Exception {
+ WeakHashMap map = new WeakHashMap();
+ for (int i = 0; i < 100; i++)
+ map.put(keyArray[i], valueArray[i]);
+
+ WeakHashMap output = new WeakHashMap();
+ map.forEach((k, v) -> output.put(k,v));
+ assertEquals(map, output);
+
+ HashSet setOutput = new HashSet();
+ map.keySet().forEach((k) -> setOutput.add(k));
+ assertEquals(map.keySet(), setOutput);
+
+ setOutput.clear();
+ map.values().forEach((v) -> setOutput.add(v));
+ assertEquals(new HashSet(map.values()), setOutput);
+
+ HashSet entrySetOutput = new HashSet();
+ map.entrySet().forEach((v) -> entrySetOutput.add(v));
+ assertEquals(map.entrySet(), entrySetOutput);
+ }
+
+ public void test_forEach_NPE() throws Exception {
+ WeakHashMap map = new WeakHashMap();
+ try {
+ map.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.keySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.values().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ try {
+ map.entrySet().forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+
+ }
+
+ public void test_forEach_CME() throws Exception {
+ WeakHashMap map = new WeakHashMap();
+ for (int i = 0; i < 100; i++)
+ map.put(keyArray[i], valueArray[i]);
+ ArrayList<Object> processed = new ArrayList<>();
+ try {
+ map.forEach(new java.util.function.BiConsumer<Object, Object>() {
+ @Override
+ public void accept(Object k, Object v) {
+ processed.add(k);
+ map.put("foo", v);
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, processed.size());
+
+ processed.clear();
+ try {
+ map.keySet().forEach(new java.util.function.Consumer<Object>() {
+ @Override
+ public void accept(Object k) {
+ processed.add(k);
+ map.put("foo2", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, processed.size());
+
+ processed.clear();
+ try {
+ map.values().forEach(new java.util.function.Consumer<Object>() {
+ @Override
+ public void accept(Object k) {
+ processed.add(k);
+ map.put("foo3", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, processed.size());
+
+ processed.clear();
+ try {
+ map.entrySet().forEach(new java.util.function.Consumer<Map.Entry<Object, Object>>() {
+ @Override
+ public void accept(Map.Entry<Object, Object> k) {
+ processed.add(k.getKey());
+ map.put("foo4", "boo");
+ }
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ // We should get a CME and DO NOT continue forEach evaluation
+ assertEquals(1, processed.size());
+ }
+
/**
* Sets up the fixture, for example, open a network connection. This method
* is called before a test is executed.
diff --git a/known_oj_tags.txt b/known_oj_tags.txt
index c92ec3c..1ac051c 100644
--- a/known_oj_tags.txt
+++ b/known_oj_tags.txt
@@ -20,3 +20,4 @@
@spec
@revised
@jls
+@implSpec
diff --git a/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java b/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java
index 625da8a..4462f81 100644
--- a/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java
+++ b/luni/src/test/java/org/apache/harmony/security/tests/java/security/ProviderTest.java
@@ -34,6 +34,7 @@
import java.security.SecurityPermission;
import java.security.Provider.Service;
import java.util.Collection;
+import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;
@@ -200,6 +201,38 @@
}
}
+ public final void testForEach() {
+ p.put("MessageDigest.SHA-1", "aaa.bbb.ccc.ddd");
+ p.put("MessageDigest.abc", "value 1");
+
+ HashMap<String, String> hm = new HashMap<>();
+ p.forEach((k,v)-> hm.put((String)k, (String)v));
+
+ assertEquals(p.size(), hm.size());
+ for(String key : hm.keySet()) {
+ assertEquals(p.get(key), hm.get(key));
+ }
+ }
+
+ public void testForEachNPE() throws Exception {
+ try {
+ p.forEach(null);
+ fail();
+ } catch(NullPointerException expected) {}
+ }
+
+ public void testForEachCME() throws Exception {
+ p.put("MessageDigest.SHA-1", "aaa.bbb.ccc.ddd");
+ p.put("MessageDigest.abc", "value 1");
+ try {
+ p.forEach(new java.util.function.BiConsumer<Object, Object>() {
+ @Override
+ public void accept(Object k, Object v) {p.put("foo", "bar");}
+ });
+ fail();
+ } catch(ConcurrentModificationException expected) {}
+ }
+
/*
* Class under test for Set keySet()
*/
diff --git a/ojluni/src/main/java/java/lang/Iterable.java b/ojluni/src/main/java/java/lang/Iterable.java
index 24efc86..05bd805 100755
--- a/ojluni/src/main/java/java/lang/Iterable.java
+++ b/ojluni/src/main/java/java/lang/Iterable.java
@@ -26,14 +26,20 @@
package java.lang;
import java.util.Iterator;
+import java.util.Objects;
+import java.util.function.Consumer;
/**
* Implementing this interface allows an object to be the target of
- * the "foreach" statement.
+ * the "for-each loop" statement. See
+ * <strong>
+ * <a href="{@docRoot}/../technotes/guides/language/foreach.html">For-each Loop</a>
+ * </strong>
*
* @param <T> the type of elements returned by the iterator
*
* @since 1.5
+ * @jls 14.14.2 The enhanced for statement
*/
public interface Iterable<T> {
@@ -43,4 +49,30 @@
* @return an Iterator.
*/
Iterator<T> iterator();
+
+ /**
+ * Performs the given action for each element of the {@code Iterable}
+ * until all elements have been processed or the action throws an
+ * exception. Unless otherwise specified by the implementing class,
+ * actions are performed in the order of iteration (if an iteration order
+ * is specified). Exceptions thrown by the action are relayed to the
+ * caller.
+ *
+ * @implSpec
+ * <p>The default implementation behaves as if:
+ * <pre>{@code
+ * for (T t : this)
+ * action.accept(t);
+ * }</pre>
+ *
+ * @param action The action to be performed for each element
+ * @throws NullPointerException if the specified action is null
+ * @since 1.8
+ */
+ default void forEach(Consumer<? super T> action) {
+ Objects.requireNonNull(action);
+ for (T t : this) {
+ action.accept(t);
+ }
+ }
}
diff --git a/ojluni/src/main/java/java/security/Provider.java b/ojluni/src/main/java/java/security/Provider.java
index 165f648..a1c8cfa 100755
--- a/ojluni/src/main/java/java/security/Provider.java
+++ b/ojluni/src/main/java/java/security/Provider.java
@@ -36,6 +36,7 @@
import java.lang.reflect.*;
import java.security.Security;
import java.security.cert.CertStoreParameters;
+import java.util.function.BiConsumer;
import javax.security.auth.login.Configuration;
@@ -379,6 +380,15 @@
return super.get(key);
}
+ /**
+ * @since 1.8
+ */
+ @Override
+ public synchronized void forEach(BiConsumer<? super Object, ? super Object> action) {
+ checkInitialized();
+ super.forEach(action);
+ }
+
// let javadoc show doc from superclass
public Enumeration<Object> keys() {
checkInitialized();
diff --git a/ojluni/src/main/java/java/util/ArrayList.java b/ojluni/src/main/java/java/util/ArrayList.java
index 57be652..3a7469d 100755
--- a/ojluni/src/main/java/java/util/ArrayList.java
+++ b/ojluni/src/main/java/java/util/ArrayList.java
@@ -26,6 +26,8 @@
package java.util;
+import java.util.function.Consumer;
+
/**
* Resizable-array implementation of the <tt>List</tt> interface. Implements
* all optional list operations, and permits all elements, including
@@ -1166,4 +1168,22 @@
return "Index: "+index+", Size: "+this.size;
}
}
+
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ Objects.requireNonNull(action);
+ final int expectedModCount = modCount;
+ @SuppressWarnings("unchecked")
+ final E[] elementData = (E[]) this.elementData;
+ final int size = this.size;
+ for (int i=0; modCount == expectedModCount && i < size; i++) {
+ action.accept(elementData[i]);
+ }
+ // Note
+ // Iterator will not throw a CME if we add something while iterating over the *last* element
+ // forEach will throw a CME in this case.
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
}
diff --git a/ojluni/src/main/java/java/util/Arrays.java b/ojluni/src/main/java/java/util/Arrays.java
index d6ce587..e32dca1 100755
--- a/ojluni/src/main/java/java/util/Arrays.java
+++ b/ojluni/src/main/java/java/util/Arrays.java
@@ -27,6 +27,7 @@
package java.util;
import java.lang.reflect.*;
+import java.util.function.Consumer;
/**
* This class contains various methods for manipulating arrays (such as
@@ -2887,6 +2888,14 @@
public boolean contains(Object o) {
return indexOf(o) != -1;
}
+
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ Objects.requireNonNull(action);
+ for (E e : a) {
+ action.accept(e);
+ }
+ }
}
/**
diff --git a/ojluni/src/main/java/java/util/Collections.java b/ojluni/src/main/java/java/util/Collections.java
index ade8c2c..7194899 100755
--- a/ojluni/src/main/java/java/util/Collections.java
+++ b/ojluni/src/main/java/java/util/Collections.java
@@ -25,10 +25,14 @@
*/
package java.util;
-import java.io.Serializable;
-import java.io.ObjectOutputStream;
+
import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.Serializable;
import java.lang.reflect.Array;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
/**
* This class consists exclusively of static methods that operate on or return
@@ -1104,6 +1108,12 @@
public void clear() {
throw new UnsupportedOperationException();
}
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ c.forEach(action);
+ }
}
/**
@@ -1388,6 +1398,12 @@
public int hashCode() {return m.hashCode();}
public String toString() {return m.toString();}
+ // Override default methods in Map
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ m.forEach(action);
+ }
+
/**
* We need this class in addition to UnmodifiableSet as
* Map.Entries themselves permit modification of the backing Map
@@ -1403,6 +1419,17 @@
UnmodifiableEntrySet(Set<? extends Map.Entry<? extends K, ? extends V>> s) {
super((Set)s);
}
+
+ static <K, V> Consumer<Map.Entry<K, V>> entryConsumer(Consumer<? super Entry<K, V>> action) {
+ return e -> action.accept(new UnmodifiableEntry<>(e));
+ }
+
+ // Override default methods in Collection
+ public void forEach(Consumer<? super Entry<K, V>> action) {
+ Objects.requireNonNull(action);
+ c.forEach(entryConsumer(action));
+ }
+
public Iterator<Map.Entry<K,V>> iterator() {
return new Iterator<Map.Entry<K,V>>() {
private final Iterator<? extends Map.Entry<? extends K, ? extends V>> i = c.iterator();
@@ -1668,6 +1695,13 @@
public String toString() {
synchronized (mutex) {return c.toString();}
}
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> consumer) {
+ synchronized (mutex) {c.forEach(consumer);}
+ }
+
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
@@ -2100,6 +2134,12 @@
public String toString() {
synchronized (mutex) {return m.toString();}
}
+ // Override default methods in Map
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ synchronized (mutex) {m.forEach(action);}
+ }
+
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
@@ -2368,6 +2408,10 @@
// element as we added it)
return c.addAll(checkedCopyOf(coll));
}
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {c.forEach(action);}
}
/**
@@ -2717,6 +2761,12 @@
return entrySet;
}
+ // Override default methods in Map
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ m.forEach(action);
+ }
+
/**
* We need this class in addition to CheckedSet as Map.Entry permits
* modification of the backing Map via the setValue operation. This
@@ -3160,6 +3210,12 @@
private Object readResolve() {
return EMPTY_SET;
}
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ Objects.requireNonNull(action);
+ }
}
/**
@@ -3233,6 +3289,12 @@
private Object readResolve() {
return EMPTY_LIST;
}
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ Objects.requireNonNull(action);
+ }
}
/**
@@ -3292,6 +3354,12 @@
private Object readResolve() {
return EMPTY_MAP;
}
+
+ // Override default methods in Map
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ }
}
// Singleton collections
@@ -3346,6 +3414,12 @@
public int size() {return 1;}
public boolean contains(Object o) {return eq(o, element);}
+
+ // Override default methods for Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ action.accept(element);
+ }
}
/**
@@ -3386,6 +3460,12 @@
throw new IndexOutOfBoundsException("Index: "+index+", Size: 1");
return element;
}
+
+ // Override default methods for Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ action.accept(element);
+ }
}
/**
@@ -3451,6 +3531,12 @@
return values;
}
+ // Override default methods in Map
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ action.accept(k, v);
+ }
+
}
// Miscellaneous
@@ -3925,6 +4011,12 @@
private static final long serialVersionUID = 2454657854757543876L;
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {
+ s.forEach(action);
+ }
+
private void readObject(java.io.ObjectInputStream stream)
throws IOException, ClassNotFoundException
{
@@ -3981,5 +4073,9 @@
public boolean removeAll(Collection<?> c) {return q.removeAll(c);}
public boolean retainAll(Collection<?> c) {return q.retainAll(c);}
// We use inherited addAll; forwarding addAll would be wrong
+
+ // Override default methods in Collection
+ @Override
+ public void forEach(Consumer<? super E> action) {q.forEach(action);}
}
}
diff --git a/ojluni/src/main/java/java/util/HashMap.java b/ojluni/src/main/java/java/util/HashMap.java
index 01b70a4..e4cad69 100755
--- a/ojluni/src/main/java/java/util/HashMap.java
+++ b/ojluni/src/main/java/java/util/HashMap.java
@@ -25,7 +25,10 @@
*/
package java.util;
+
import java.io.*;
+import java.util.function.Consumer;
+import java.util.function.BiConsumer;
/**
* Hash table based implementation of the <tt>Map</tt> interface. This
@@ -1027,6 +1030,25 @@
public void clear() {
HashMap.this.clear();
}
+ public final void forEach(Consumer<? super K> action) {
+ HashMapEntry<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ action.accept(e.key);
+ // Android-modified - this was outside of the loop, inconsistent with other
+ // collections
+ if (modCount != mc) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
+ }
+ }
}
/**
@@ -1060,6 +1082,24 @@
public void clear() {
HashMap.this.clear();
}
+ public final void forEach(Consumer<? super V> action) {
+ HashMapEntry<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ action.accept(e.value);
+ // Android-modified - this was outside of the loop, inconsistent with other
+ // collections
+ if (modCount != mc) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
+ }
}
/**
@@ -1109,6 +1149,44 @@
public void clear() {
HashMap.this.clear();
}
+ public final void forEach(Consumer<? super Map.Entry<K,V>> action) {
+ HashMapEntry<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ action.accept(e);
+ // Android-modified - this was outside of the loop, inconsistent with other
+ // collections
+ if (modCount != mc) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ HashMapEntry<K,V>[] tab;
+ if (action == null)
+ throw new NullPointerException();
+ if (size > 0 && (tab = table) != null) {
+ int mc = modCount;
+ for (int i = 0; i < tab.length; ++i) {
+ for (HashMapEntry<K,V> e = tab[i]; e != null; e = e.next) {
+ action.accept(e.key, e.value);
+ // Android-modified - this was outside of the loop, inconsistent with other
+ // collections
+ if (modCount != mc) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
}
/**
diff --git a/ojluni/src/main/java/java/util/Hashtable.java b/ojluni/src/main/java/java/util/Hashtable.java
index a88bda1..57d9587 100755
--- a/ojluni/src/main/java/java/util/Hashtable.java
+++ b/ojluni/src/main/java/java/util/Hashtable.java
@@ -25,7 +25,9 @@
*/
package java.util;
+
import java.io.*;
+import java.util.function.BiConsumer;
/**
* This class implements a hash table, which maps keys to values. Any
@@ -921,6 +923,26 @@
return h;
}
+ @SuppressWarnings("unchecked")
+ @Override
+ public synchronized void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action); // explicit check required in case
+ // table is empty.
+ final int expectedModCount = modCount;
+
+ HashtableEntry<?, ?>[] tab = table;
+ for (HashtableEntry<?, ?> entry : tab) {
+ while (entry != null) {
+ action.accept((K)entry.key, (V)entry.value);
+ entry = entry.next;
+
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
+
/**
* Save the state of the Hashtable to a stream (i.e., serialize it).
*
diff --git a/ojluni/src/main/java/java/util/IdentityHashMap.java b/ojluni/src/main/java/java/util/IdentityHashMap.java
index 95ff9f0..b0847ec 100755
--- a/ojluni/src/main/java/java/util/IdentityHashMap.java
+++ b/ojluni/src/main/java/java/util/IdentityHashMap.java
@@ -24,7 +24,10 @@
*/
package java.util;
+
import java.io.*;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
* This class implements the <tt>Map</tt> interface with a hash table, using
@@ -1240,4 +1243,24 @@
tab[i] = k;
tab[i + 1] = value;
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ int expectedModCount = modCount;
+
+ Object[] t = table;
+ for (int index = 0; index < t.length; index += 2) {
+ Object k = t[index];
+ if (k != null) {
+ action.accept((K) unmaskNull(k), (V) t[index + 1]);
+ }
+
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
}
diff --git a/ojluni/src/main/java/java/util/LinkedHashMap.java b/ojluni/src/main/java/java/util/LinkedHashMap.java
index 9a7eefe..c1b2644 100755
--- a/ojluni/src/main/java/java/util/LinkedHashMap.java
+++ b/ojluni/src/main/java/java/util/LinkedHashMap.java
@@ -25,7 +25,10 @@
*/
package java.util;
+
import java.io.*;
+import java.util.function.Consumer;
+import java.util.function.BiConsumer;
/**
* <p>Hash table and linked list implementation of the <tt>Map</tt> interface,
@@ -502,4 +505,16 @@
protected boolean removeEldestEntry(Map.Entry<K,V> eldest) {
return false;
}
+
+ // Map overrides
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ if (action == null)
+ throw new NullPointerException();
+ int mc = modCount;
+ // Android modified - breaks from the loop when modCount != mc
+ for (LinkedHashMapEntry<K,V> e = header.after; modCount == mc && e != header; e = e.after)
+ action.accept(e.key, e.value);
+ if (modCount != mc)
+ throw new ConcurrentModificationException();
+ }
}
diff --git a/ojluni/src/main/java/java/util/Map.java b/ojluni/src/main/java/java/util/Map.java
index ccdb288..afb24ba 100755
--- a/ojluni/src/main/java/java/util/Map.java
+++ b/ojluni/src/main/java/java/util/Map.java
@@ -25,6 +25,12 @@
package java.util;
+
+import java.util.function.BiConsumer;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+
/**
* An object that maps keys to values. A map cannot contain duplicate keys;
* each key can map to at most one value.
@@ -475,4 +481,44 @@
*/
int hashCode();
+ /**
+ * Performs the given action for each entry in this map until all entries
+ * have been processed or the action throws an exception. Unless
+ * otherwise specified by the implementing class, actions are performed in
+ * the order of entry set iteration (if an iteration order is specified.)
+ * Exceptions thrown by the action are relayed to the caller.
+ *
+ * @implSpec
+ * The default implementation is equivalent to, for this {@code map}:
+ * <pre> {@code
+ * for (Map.Entry<K, V> entry : map.entrySet())
+ * action.accept(entry.getKey(), entry.getValue());
+ * }</pre>
+ *
+ * The default implementation makes no guarantees about synchronization
+ * or atomicity properties of this method. Any implementation providing
+ * atomicity guarantees must override this method and document its
+ * concurrency properties.
+ *
+ * @param action The action to be performed for each entry
+ * @throws NullPointerException if the specified action is null
+ * @throws ConcurrentModificationException if an entry is found to be
+ * removed during iteration
+ * @since 1.8
+ */
+ default void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ for (Map.Entry<K, V> entry : entrySet()) {
+ K k;
+ V v;
+ try {
+ k = entry.getKey();
+ v = entry.getValue();
+ } catch(IllegalStateException ise) {
+ // this usually means the entry is no longer in the map.
+ throw new ConcurrentModificationException(ise);
+ }
+ action.accept(k, v);
+ }
+ }
}
diff --git a/ojluni/src/main/java/java/util/TreeMap.java b/ojluni/src/main/java/java/util/TreeMap.java
index f25e5af..d9235a9 100755
--- a/ojluni/src/main/java/java/util/TreeMap.java
+++ b/ojluni/src/main/java/java/util/TreeMap.java
@@ -26,6 +26,9 @@
package java.util;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
+
/**
* A Red-Black tree based {@link NavigableMap} implementation.
* The map is sorted according to the {@linkplain Comparable natural
@@ -965,6 +968,19 @@
return tailMap(fromKey, true);
}
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ int expectedModCount = modCount;
+ for (TreeMapEntry<K, V> e = getFirstEntry(); e != null; e = successor(e)) {
+ action.accept(e.key, e.value);
+
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+
// View class support
class Values extends AbstractCollection<V> {
diff --git a/ojluni/src/main/java/java/util/Vector.java b/ojluni/src/main/java/java/util/Vector.java
index 0d69591..f2c919f 100755
--- a/ojluni/src/main/java/java/util/Vector.java
+++ b/ojluni/src/main/java/java/util/Vector.java
@@ -25,6 +25,8 @@
package java.util;
+import java.util.function.Consumer;
+
/**
* The {@code Vector} class implements a growable array of
* objects. Like an array, it contains components that can be
@@ -1209,4 +1211,19 @@
lastRet = -1;
}
}
+
+ @Override
+ public synchronized void forEach(Consumer<? super E> action) {
+ Objects.requireNonNull(action);
+ final int expectedModCount = modCount;
+ @SuppressWarnings("unchecked")
+ final E[] elementData = (E[]) this.elementData;
+ final int elementCount = this.elementCount;
+ for (int i=0; modCount == expectedModCount && i < elementCount; i++) {
+ action.accept(elementData[i]);
+ }
+ if (modCount != expectedModCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
}
diff --git a/ojluni/src/main/java/java/util/WeakHashMap.java b/ojluni/src/main/java/java/util/WeakHashMap.java
index 652b053..fa5dace 100755
--- a/ojluni/src/main/java/java/util/WeakHashMap.java
+++ b/ojluni/src/main/java/java/util/WeakHashMap.java
@@ -27,6 +27,8 @@
package java.util;
import java.lang.ref.WeakReference;
import java.lang.ref.ReferenceQueue;
+import java.util.function.BiConsumer;
+import java.util.function.Consumer;
/**
@@ -1077,4 +1079,26 @@
return deepCopy().toArray(a);
}
}
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public void forEach(BiConsumer<? super K, ? super V> action) {
+ Objects.requireNonNull(action);
+ int expectedModCount = modCount;
+
+ Entry<K, V>[] tab = getTable();
+ for (Entry<K, V> entry : tab) {
+ while (entry != null) {
+ Object key = entry.get();
+ if (key != null) {
+ action.accept((K)WeakHashMap.unmaskNull(key), entry.value);
+ }
+ entry = entry.next;
+
+ if (expectedModCount != modCount) {
+ throw new ConcurrentModificationException();
+ }
+ }
+ }
+ }
}