为什么CopyOnWriteArrayList的迭代器允许在增强型for循环中使用remove(),而它的迭代器不支持remove()操作?

Why CopyOnWriteArrayList's iterator allows remove() in enhanced-for loop, while its iterator does not support remove() operation?

CopyOnWriteArrayList

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) 方法。

如果您尝试使用 iteratorCopyOnWriteArrayList 中删除一个元素。它会抛出异常。这就是 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")));