迭代器删除抛出 ConcurrentException

问题描述 投票:0回答:4

试图在列表上实施就地清除。

        Iterator<Integer> itr = ls.iterator();
        for (Integer x : ls) {
            int count = 0;
            while(itr.hasNext()){
                if (itr.next().equals(x)) {
                    count++;
                    if (count > 1) {
                        itr.remove();
                    }
                }
            }
        }

线程“main”中的异常 java.util.ConcurrentModificationException

        Iterator<Integer> iterator = numbers.iterator();
        while (iterator.hasNext()) {
            if (iterator.next() == 1) {
                iterator.remove(); // Doesn't throw exception
            }
        }

然而,只有一个循环认为它没有。为什么?

这种行为的原因是什么

java iterator concurrentmodification
4个回答
2
投票

这里有两个迭代器

ls

  • 通过
    ls.iterator()
    明确创建的;
  • 通过
    for (Integer x : ls)
    隐式创建的。

如果您在第一个迭代器上调用

remove()
,它会使第二个迭代器无效,这意味着在第二个迭代器上调用
next()
会导致(或容易导致)抛出
ConcurrentModificationException


进行就地重复数据删除的可能方法可能是这样的:

int dst = 0;
for (Integer x : ls) {
  if (!ls.subList(0, dst).contains(x)) {
    ls.set(dst++, x);
  }
}
ls.subList(dst, ls.size()).clear();

这会将之前没有见过的元素移到数组的左侧;然后从数组中删除所有其他元素。

在循环中更改列表不会导致

ConcurrentModificationException
因为没有对列表进行结构修改。


2
投票

为了研究这个问题,我们需要研究

Java enhanced for loop
内部是如何工作的。

Java 增强的 for 循环在内部使用一个

Fail Fast Iterator
,它使用一个名为
modCount
的内部标志(每次修改集合时都会更新)来跟踪集合的结构是否被操纵。如果有任何变化,那么它就会抛出
ConcurrentModificationException
.

增强的for循环请看下面的同义词代码

 List<Integer> ls = Arrays.asList(1,2,3);

 for (Integer x : ls) {

 }

具有内码结构的同义码

for (Iterator<Integer> itr = ls.iterator(); itr.hasNext();) {
   Integer x = itr.next(); 
}

由于上述代码结构,增强的for循环不允许在迭代时删除元素。

即使没有明确引用 for 迭代器来调用 remove() 方法,如果在 List 上调用 remove() 方法,则会触发 ConcurrentModificationException。

最后,

ConcurrentModificationException
的原因应该是你在迭代器上调用 remove() 方法
itr
,它在同一个列表
ls
上工作,同时调用与同一个列表相关的增强 for 循环
ls
.


0
投票

这是避免并发 mod 异常的另一种可能性。要删除重复项,请使用集合。

 List<String> list = new ArrayList<>(List.of("A","B","C","D","A","B","B","E"));
 Set<String> set = new LinkedHashSet<>();
 set.addAll(list);
 System.out.println(set);

版画

[A, B, C, D, E]

注意:可以使用任何设置。链接的哈希集保留插入顺序。


0
投票

我在实施这个算法时遇到了一个小问题

purgeInplace(Arrays.asList(1, 2, 3, 1))

投掷

Exception in thread "main" java.lang.UnsupportedOperationException
        at java.base/java.util.AbstractList.remove(AbstractList.java:169)
        at java.base/java.util.AbstractList$Itr.remove(AbstractList.java:389)
        at java.base/java.util.AbstractList.removeRange(AbstractList.java:600)
        at java.base/java.util.AbstractList$SubList.removeRange(AbstractList.java:813)     
        at java.base/java.util.AbstractList.clear(AbstractList.java:245)
        at PurgeUtil.purgeInplace(PurgeUtil.java:25)
        at PurgeUtil.main(PurgeUtil.java:15)

然而, purgeInplace(new ArrayList<>(Arrays.asList(1, 2, 3, 1)));

工作正常。

附注。由于字数限制无法将此放在评论下。

© www.soinside.com 2019 - 2024. All rights reserved.