为什么CopyOnWriteArrayList的迭代器允许在增强型for循环中使用remove(),而它的迭代器不支持remove()操作?
Why CopyOnWriteArrayList's iterator allows remove() in enhanced-for loop, while its iterator does not support remove() operation?
The iterator does NOT support the remove method.
但为什么它在增强型 for 循环中起作用?
List<String> lst = new CopyOnWriteArrayList<>();
lst.add("one");
lst.add("two");
lst.add("three");
for (String str : lst) {
if (str.equals("one")) {
lst.remove("two"); // no ConcurrentModificationException
}
}
System.out.println(lst); // [one, three] => removed "two" !
List<String> lst = new ArrayList<>();
会生成 ConcurrentModificationException
Javadoc 明确指出 CopyOnWriteArrayList.iterator()
不支持 remove()
=> 它会抛出 UnsupportedOperationException
!我知道它是弱一致的 - 如果我从 CopyOnWriteArrayList 获得迭代器后向 CopyOnWriteArrayList 添加元素,那么就没有 ConcurrentModificationException
P.S。 抱歉,我不专心 - 我没有在迭代器上调用 remove()!我对在 enhanced-for 内部感到困惑(它隐式使用迭代器)。
CopyOnWriteArrayList 迭代器故障安全实现支持修改操作。
当您迭代 CopyOnWriteArrayList 和 CopyOnWriteArraySet 时,迭代器使用基础列表(或集合)的快照,并且不反映创建快照后对列表或集合的任何更改。迭代器永远不会抛出 ConcurrentModificationException。
阅读更多信息:https://markusjais.com/java-concurrency-understanding-copyonwritearraylist-and-copyonwritearrayset/
顺便说一下,在 ArrayList() 等经典 List 实现中,您不需要显式使用迭代器。使用 list.removeIf(predicate) 方法。
如果您尝试使用 iterator
从 CopyOnWriteArrayList
中删除一个元素。它会抛出异常。这就是 javadoc 试图表达的意思。
代码:
List<String> lst2 = new CopyOnWriteArrayList<String>();
lst2.add("one");
lst2.add("two");
lst2.add("three");
Iterator<String> iterator2 = lst2.iterator();
while (iterator2.hasNext()) {
if (iterator2.next().equals("two")) {
iterator2.remove();
}
}
System.out.println(lst2.toString());
输出:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1178)
at MyClass.main(MyClass.java:29)
但是,如果您在 Arraylist
上做同样的事情。它会工作正常。
来源:ArrayList
代码:
List<String> lst = new ArrayList<String>();
lst.add("one");
lst.add("two");
lst.add("three");
Iterator<String> iterator = lst.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals("two")) {
iterator.remove();
}
}
System.out.println(lst.toString());
输出:
[one, three]
虽然如果你不想使用迭代器你可以只使用 public boolean removeIf(Predicate<? super E> filter)
这只是一行并给出与上面相同的输出。
lst.removeIf(n -> (n.equals("two")));
The iterator does NOT support the remove method.
但为什么它在增强型 for 循环中起作用?
List<String> lst = new CopyOnWriteArrayList<>();
lst.add("one");
lst.add("two");
lst.add("three");
for (String str : lst) {
if (str.equals("one")) {
lst.remove("two"); // no ConcurrentModificationException
}
}
System.out.println(lst); // [one, three] => removed "two" !
List<String> lst = new ArrayList<>();
会生成 ConcurrentModificationException
Javadoc 明确指出 CopyOnWriteArrayList.iterator()
不支持 remove()
=> 它会抛出 UnsupportedOperationException
!我知道它是弱一致的 - 如果我从 CopyOnWriteArrayList 获得迭代器后向 CopyOnWriteArrayList 添加元素,那么就没有 ConcurrentModificationException
P.S。 抱歉,我不专心 - 我没有在迭代器上调用 remove()!我对在 enhanced-for 内部感到困惑(它隐式使用迭代器)。
CopyOnWriteArrayList 迭代器故障安全实现支持修改操作。
当您迭代 CopyOnWriteArrayList 和 CopyOnWriteArraySet 时,迭代器使用基础列表(或集合)的快照,并且不反映创建快照后对列表或集合的任何更改。迭代器永远不会抛出 ConcurrentModificationException。
阅读更多信息:https://markusjais.com/java-concurrency-understanding-copyonwritearraylist-and-copyonwritearrayset/
顺便说一下,在 ArrayList() 等经典 List 实现中,您不需要显式使用迭代器。使用 list.removeIf(predicate) 方法。
如果您尝试使用 iterator
从 CopyOnWriteArrayList
中删除一个元素。它会抛出异常。这就是 javadoc 试图表达的意思。
代码:
List<String> lst2 = new CopyOnWriteArrayList<String>();
lst2.add("one");
lst2.add("two");
lst2.add("three");
Iterator<String> iterator2 = lst2.iterator();
while (iterator2.hasNext()) {
if (iterator2.next().equals("two")) {
iterator2.remove();
}
}
System.out.println(lst2.toString());
输出:
Exception in thread "main" java.lang.UnsupportedOperationException
at java.util.concurrent.CopyOnWriteArrayList$COWIterator.remove(CopyOnWriteArrayList.java:1178)
at MyClass.main(MyClass.java:29)
但是,如果您在 Arraylist
上做同样的事情。它会工作正常。
来源:ArrayList
代码:
List<String> lst = new ArrayList<String>();
lst.add("one");
lst.add("two");
lst.add("three");
Iterator<String> iterator = lst.iterator();
while (iterator.hasNext()) {
if (iterator.next().equals("two")) {
iterator.remove();
}
}
System.out.println(lst.toString());
输出:
[one, three]
虽然如果你不想使用迭代器你可以只使用 public boolean removeIf(Predicate<? super E> filter)
这只是一行并给出与上面相同的输出。
lst.removeIf(n -> (n.equals("two")));