简体   繁体   English

从列表中删除项目时出现 ConcurrentModificationException

[英]ConcurrentModificationException when removing item from a list

I have an application with custom list class.我有一个带有自定义列表类的应用程序。 When trying to do a foreach function with customer argument following happens:当尝试使用客户参数执行 foreach 函数时,会发生以下情况:

Important!重要的! I cannot modify code in main我无法修改 main 中的代码

Main:主要的:

XList<Integer> lmod = XList.of(1,2,8, 10, 11, 30, 3, 4);
lmod.forEachWithIndex( (e, i) -> lmod.set(i, e*2));
System.out.println(lmod);
lmod.forEachWithIndex( (e, i) -> { if (i % 2 == 0) lmod.remove(e); } );
System.out.println(lmod);
lmod.forEachWithIndex( (e, i) -> { if (i % 2 == 0) lmod.remove(i); } );
System.out.println(lmod);

XList class: XList 类:

public class XList <T> extends ArrayList<T> {
public XList(Collection<T> collection) {
    super(collection);
}

public XList(T... ints) {
    super(Arrays.asList(ints));
}

public static <T> XList<T> of(Set<T> set) {
    return new XList<>(set);
}

public static <T> XList<T> of(T... ints) {
    return new XList<>(ints);
}

public void forEachWithIndex(BiConsumer<? super T, ? super Integer> consumer) {
    Iterator<T> iterator = this.iterator();

    int counter = 0;

    while (iterator.hasNext()) {
        consumer.accept(iterator.next(), counter);
        counter++;
    }
}

Error:错误:

Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at zad1.XList.forEachWithIndex(XList.java:126)
at zad1.Main.main(Main.java:89)

ConcurrentModificationException means: ConcurrentModificationException 意味着:

  1. At point in time A, you make an iterator of some collection either by invoking its .iterator() method, or having a for (var x : collection) {} call it for you.在时间点 A,您可以通过调用其.iterator()方法或让for (var x : collection) {}为您调用它来.iterator()某个集合的迭代器。
  2. At point in time B, you change the collection (and not via the iterator you made in A's .remove() method), for example by invoking remove or add or clear or retainAll .在时间点 B,您更改集合(而不是通过您在 A 的.remove()方法中创建的迭代器),例如通过调用removeaddclearretainAll
  3. At point in time C, you so much at look funny at that iterator: You invoke any of its methods, or you have the for loop do it by hitting the } of its block.在时间点 C,你对那个迭代器看起来很有趣:你调用它的任何方法,或者你让 for 循环通过点击它的块的}来完成它。

What you need to do is decidedly nontrivial!你需要做的绝对是不平凡的!

Think about it, given an initial list of [A, B, C, D, E]: you'd presumably want that forEachWithIndex method to get run 5 times, regardless of what happens to the list in between: [0, A], [1, B], [2, C], [3, D], and [4, E].考虑一下,给定一个 [A, B, C, D, E] 的初始列表:您可能希望forEachWithIndex方法运行 5 次,无论列表之间发生了什么:[0, A] , [1, B], [2, C], [3, D], 和 [4, E]。 So what should happen if, during the loop for [0, A] , you remove C?那么如果在[0, A]的循环中删除 C 会发生什么?

There's an argument to be made that the [2, C] event shouldn't happen at all, and, in fact, that the remaining loops should be [1, B] , [2, D] , and [3, E] .有一个论点认为[2, C]事件根本不应该发生,事实上,剩余的循环应该是[1, B][2, D][3, E] . It's because this is so hard to answer that java solved the problem in the iterator() API by simply not allowing you to do it!这是因为这个问题很难回答,所以 java 通过简单地不允许你这样做来解决iterator() API 中的问题!

Similar question comes up when you call .add("F") during the loop for [0, A] .当您在[0, A]的循环中调用.add("F")时,会出现类似的问题。 Should the for loop run the lambda once with arguments [5, F] , or not? for 循环是否应该使用参数[5, F]运行一次 lambda? An open question.一个开放的问题。

It's up to you to answer this question, and you should document this in excruciating detail.由您来回答这个问题,您应该详细地记录下来。 No matter which choice you make it'll be quite difficult!无论你做出哪个选择,都将是相当困难的!

I think the for loop should include the changes我认为 for 循环应该包括更改

This is incredibly complicated.这是非常复杂的。 Because imagine that the loop for C ends up removing A. That means that your list will first invoke the lambda with arguments [0, A] , [1, B] , and [2, C] , and then, what's the next iteration going to look like?因为想象 C 的循环最终删除了 A。这意味着您的列表将首先使用参数[0, A][1, B][2, C]调用 lambda,然后,下一次迭代是什么会是什么样子? Presumably the only sane answer then is [2, D] .大概唯一合理的答案是[2, D] To make this work you need to track all sorts of things - the list's loop code needs to be aware that deletions happened and that therefore it needs to 'down-adjust' (because you can't simply loop from 0 to 'list size', if you did that, the next iteration would be [3, E] and you have skipped D entirely even though it is still in that list.为了完成这项工作,您需要跟踪各种事情 - 列表的循环代码需要知道删除发生了,因此它需要“向下调整”(因为您不能简单地从 0 循环到“列表大小” ,如果你这样做了,下一次迭代将是[3, E]并且你已经完全跳过了 D,即使它仍然在那个列表中。

Make some coffee, dig in, find a whiteboard, sketch this out.煮点咖啡,深入研究,找一块白板,勾勒出来。 Reserve a full day and be aware that the code will be many pages to deal with it all.预留一整天,并注意代码将有很多页来处理这一切。 Make a TON of test cases and describe in detail precisely what you expect to happen for all of these.制作大量测试用例,并准确描述您对所有这些用例的期望。

Hmm, okay, nevermind.嗯,好吧,没关系。 Let's say it should iterate for all elements the original had no matter what changes假设无论发生什么变化,它都应该迭代原始元素的所有元素

This is easier but inefficient.这更容易但效率低下。 The fix is simple: First make a copy of the list.解决方法很简单:首先制作列表的副本。 Then iterate the copy.然后迭代复制。 The copy cannot change (you're the only one with a ref to it), so they can do whatever they want to the underlying list:该副本无法更改(您是唯一一个对其有引用的人),因此他们可以对基础列表执行任何他们想做的事情:

XList<T> copy = new XList<T>(this);
int counter = 0;

while (iterator.hasNext()) consumer.accept(iterator.next(), counter++);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 从列表中删除项目 - 没有得到 ConcurrentModificationException - Removing item from a list inside for - didn't get ConcurrentModificationException 在嵌套循环中删除项目时发生ConcurrentModificationException - ConcurrentModificationException when removing item in nested loops 如何修复ConcurrentModificationException,从arrayList中删除项目? - How to fix ConcurrentModificationException, removing item from arrayList? 从游戏中删除子弹时出现ConcurrentModificationException - ConcurrentModificationException when removing bullet from game 从队列中添加/删除时获取 ConcurrentModificationException - Getting ConcurrentModificationException when adding/removing from queue ConcurrentModificationException 使用列表迭代器 java 删除元素时 - ConcurrentModificationException When removing element using list iterator java 为什么使用迭代器从列表中删除元素会导致ConcurrentModificationException? - Why removing element from list using iterator causes ConcurrentModificationException? 尝试从列表中获取元素时发生ConcurrentModificationException - ConcurrentModificationException when attempting to get an element from a List 从 ArrayLists 中删除对象时如何避免 concurrentModificationException” - how to avoid concurrentModificationException when removing an object from to ArrayLists" 从ArrayList中删除对象时如何避免ConcurrentModificationException - How to avoid a ConcurrentModificationException when removing objects from an ArrayList
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM