【问题标题】:Why CopyOnWriteArrayList's iterator allows remove() in enhanced-for loop, while its iterator does not support remove() operation?为什么 CopyOnWriteArrayList 的迭代器在增强型 for 循环中允许 remove(),而它的迭代器不支持 remove() 操作?
【发布时间】:2020-01-09 15:49:19
【问题描述】:

CopyOnWriteArrayList

迭代器不支持 remove 方法。

但为什么它在增强的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&lt;String&gt; lst = new ArrayList&lt;&gt;(); 会生成ConcurrentModificationException

Javadoc 明确指出CopyOnWriteArrayList.iterator() 不支持remove() => 它将抛出UnsupportedOperationException!我知道它是弱一致的 - 如果我在从 CopyOnWriteArrayList

获得迭代器后向 CopyOnWriteArrayList 添加元素,则说没有 ConcurrentModificationException

附: 抱歉,我没有专心——我没有在迭代器上调用 remove()!我被内部增强型弄糊涂了(它隐式使用了迭代器)。

【问题讨论】:

  • 注意代码在列表本身而不是迭代器上调用remove(),因此代码与上面的引号不匹配。报价准确。
  • @jaco0646 这应该是答案!对不起,我不专心。当然,CopyOnWriteArrayList 是弱一致的,因此 ConcurrentModificationException 是不可能的!

标签: java for-loop iterator copyonwritearraylist


【解决方案1】:

CopyOnWriteArrayList 迭代器故障安全实现支持修改操作。

当您迭代 CopyOnWriteArrayList 和 CopyOnWriteArraySet 时,迭代器使用基础列表(或集合)的快照,并且在创建快照后不会反映对列表或集合的任何更改。迭代器永远不会抛出 ConcurrentModificationException。

阅读更多:https://markusjais.com/java-concurrency-understanding-copyonwritearraylist-and-copyonwritearrayset/

顺便说一句,在 ArrayList() 等经典 List 实现中,您不需要显式使用迭代器。使用 list.removeIf(predicate) 方法。

【讨论】:

  • 但是 javadoc 明确指出 CopyOnWriteArrayList.iterator() 不支持 remove() => 它将抛出 UnsupportedOperationException!我知道它是弱一致的 - 如果我在从 CopyOnWriteArrayList 获得迭代器后向 CopyOnWriteArrayList 添加元素,则说没有 ConcurrentModificationException ...
  • @CodeComplete 但是你到底在哪里使用iterator.remove?您正在使用 list.remove ,它不使用 for-each 使用的迭代器,因此您的报价不适用于它。
  • @Pshemo 对不起,我的注意力不集中。你是对的!
  • @CodeComplete 没问题。这是该网站存在的主要原因之一 :)
【解决方案2】:

如果您尝试使用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&lt;? super E&gt; filter),这只是一行,并提供与上面相同的输出。

lst.removeIf(n -> (n.equals("two")));

【讨论】:

    猜你喜欢
    • 2010-11-04
    • 2015-11-03
    • 1970-01-01
    • 2023-03-30
    • 1970-01-01
    • 2019-01-05
    • 1970-01-01
    • 1970-01-01
    • 2012-07-12
    相关资源
    最近更新 更多