试图在列表上实施就地清除。
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
}
}
然而,只有一个循环认为它没有。为什么?
这种行为的原因是什么
这里有两个迭代器
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
因为没有对列表进行结构修改。
为了研究这个问题,我们需要研究
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
.
这是避免并发 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]
注意:可以使用任何设置。链接的哈希集保留插入顺序。
我在实施这个算法时遇到了一个小问题
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)));
工作正常。
附注。由于字数限制无法将此放在评论下。