首页 文章

在迭代和从ArrayList中删除元素时,如何避免java.util.ConcurrentModificationException

提问于
浏览
141

我有一个我想迭代的ArrayList . 迭代它时,我必须同时删除元素 . 显然这会引发 java.util.ConcurrentModificationException .

处理此问题的最佳做法是什么?我应该先克隆列表吗?

我删除不在循环本身但是代码的另一部分的元素 .

我的代码看起来像这样:

public class Test() {
    private ArrayList<A> abc = new ArrayList<A>();

    public void doStuff() {
        for (A a : abc) 
        a.doSomething();
    }

    public void removeA(A a) {
        abc.remove(a);
    }
}

a.doSomething 可能会调用 Test.removeA() ;

17 回答

  • 6

    来自ArrayList的JavaDocs

    此类的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException .

  • 242

    你应该以传统的方式迭代数组

    每次从列表中删除元素时,后面的元素都将向前推进 . 只要您不更改迭代之外的元素,以下代码就可以工作 .

    public class Test(){
        private ArrayList<A> abc = new ArrayList<A>();
    
        public void doStuff(){
            for(int i = (abc.size() - 1); i >= 0; i--) 
                abc.get(i).doSomething();
        }
    
        public void removeA(A a){
            abc.remove(a);
        }
    }
    
  • 0

    以正常方式执行循环, java.util.ConcurrentModificationException 是与访问的元素相关的错误 .

    所以尝试:

    for(int i = 0; i < list.size(); i++){
        lista.get(i).action();
    }
    
  • 0

    两种选择:

    • 创建一个要删除的值列表,在循环中添加到该列表,然后在末尾调用 originalList.removeAll(valuesToRemove)

    • 在迭代器本身上使用 remove() 方法 . 请注意,这意味着您无法使用增强型for循环 .

    作为第二个选项的示例,从列表中删除长度大于5的任何字符串:

    List<String> list = new ArrayList<String>();
    ...
    for (Iterator<String> iterator = list.iterator(); iterator.hasNext(); ) {
        String value = iterator.next();
        if (value.length() > 5) {
            iterator.remove();
        }
    }
    
  • 5

    您试图从高级“for循环”中的列表中删除值,这是不可能的,即使您应用任何技巧(您在代码中执行) . 更好的方法是将迭代器级别编码为其他建议 .

    我想知道人们怎么没有建议传统的for循环方法 .

    for( int i = 0; i < lStringList.size(); i++ )
    {
        String lValue = lStringList.get( i );
        if(lValue.equals("_Not_Required"))
        {
             lStringList.remove(lValue);
             i--; 
        }  
    }
    

    这也有效 .

  • 4

    一种选择是将 removeA 方法修改为 -

    public void removeA(A a,Iterator<A> iterator) {
         iterator.remove(a);
         }
    

    但这意味着你的 doSomething() 应该能够将 iterator 传递给 remove 方法 . 不是一个好主意 .

    你能用两步法做到这一点:在迭代列表的第一个循环中,不是删除所选元素,而是将它们标记为要删除 . 为此,您可以简单地将这些元素(浅拷贝)复制到另一个 List 中 .

    然后,一旦完成迭代,只需从第一个列表中执行 removeAll 第二个列表中的所有元素 .

  • 5

    这是一个示例,我使用不同的列表添加要删除的对象,然后我使用stream.foreach从原始列表中删除元素:

    private ObservableList<CustomerTableEntry> customersTableViewItems = FXCollections.observableArrayList();
    ...
    private void removeOutdatedRowsElementsFromCustomerView()
    {
        ObjectProperty<TimeStamp> currentTimestamp = new SimpleObjectProperty<>(TimeStamp.getCurrentTime());
        long diff;
        long diffSeconds;
        List<Object> objectsToRemove = new ArrayList<>();
        for(CustomerTableEntry item: customersTableViewItems) {
            diff = currentTimestamp.getValue().getTime() - item.timestamp.getValue().getTime();
            diffSeconds = diff / 1000 % 60;
            if(diffSeconds > 10) {
                // Element has been idle for too long, meaning no communication, hence remove it
                System.out.printf("- Idle element [%s] - will be removed\n", item.getUserName());
                objectsToRemove.add(item);
            }
        }
        objectsToRemove.stream().forEach(o -> customersTableViewItems.remove(o));
    }
    
  • 2

    在Java 8中,您可以使用Collection Interface并通过调用removeIf方法执行此操作:

    yourList.removeIf((A a) -> a.value == 2);
    

    更多信息可以在here找到

  • 0

    像这样做简单:

    for (Object object: (ArrayList<String>) list.clone()) {
        list.remove(object);
    }
    
  • 6

    而不是使用For循环,使用normal for循环 . 例如,下面的代码删除了数组列表中的所有元素,而没有给出java.util.ConcurrentModificationException . 您可以根据用例修改循环中的条件 .

    for(int i=0;i<abc.size();i++)  {
    
              e.remove(i);
            }
    
  • 0

    “我应该首先克隆列表吗?”

    这将是最简单的解决方案,从克隆中删除,并在删除后复制克隆 .

    我的rummikub游戏的一个例子:

    SuppressWarnings("unchecked")
    public void removeStones() {
      ArrayList<Stone> clone = (ArrayList<Stone>) stones.clone();
      // remove the stones moved to the table
      for (Stone stone : stones) {
          if (stone.isOnTable()) {
             clone.remove(stone);
          }
      }
      stones = (ArrayList<Stone>) clone.clone();
      sortStones();
    }
    
  • 7

    使用流的替代Java 8解决方案:

    theList = theList.stream()
                .filter(element -> !shouldBeRemoved(element))
                .collect(Collectors.toList());
    

    在Java 7中,您可以使用Guava代替:

    theList = FluentIterable.from(theList)
                .filter(new Predicate<String>() {
                    @Override
                    public boolean apply(String element) {
                        return !shouldBeRemoved(element);
                    }
                })
                .toImmutableList();
    

    请注意,Guava示例会生成一个不可变列表,该列表可能是您想要的,也可能不是 .

  • 14

    在迭代列表时,如果要删除元素是可能的 . 让我看看下面的例子,

    ArrayList<String>  names = new ArrayList<String>();
            names.add("abc");
            names.add("def");
            names.add("ghi");
            names.add("xyz");
    

    我有上面的数组列表名称 . 我想从上面的列表中删除“def”名称,

    for(String name : names){
        if(name.equals("def")){
            names.remove("def");
        }
    }
    

    上面的代码抛出 ConcurrentModificationException 异常,因为您在迭代时修改列表 .

    所以,通过这种方式从Arraylist中删除“def”名称,

    Iterator<String> itr = names.iterator();            
    while(itr.hasNext()){
        String name = itr.next();
        if(name.equals("def")){
            itr.remove();
        }
    }
    

    上面的代码,通过迭代器我们可以从Arraylist中删除“def”名称并尝试打印数组,你会看到下面的输出 .

    输出:[abc,ghi,xyz]

  • -2

    如果您的目标是从列表中删除所有元素,则可以迭代每个项目,然后调用:

    list.clear()
    
  • 2

    我迟到了,但我回答这个问题,因为我认为这个解决方案简单而优雅:

    List<String> listFixed = new ArrayList<String>();
    List<String> dynamicList = new ArrayList<String>();
    
    public void fillingList() {
        listFixed.add("Andrea");
        listFixed.add("Susana");
        listFixed.add("Oscar");
        listFixed.add("Valeria");
        listFixed.add("Kathy");
        listFixed.add("Laura");
        listFixed.add("Ana");
        listFixed.add("Becker");
        listFixed.add("Abraham");
        dynamicList.addAll(listFixed);
    }
    
    public void updatingListFixed() {
        for (String newList : dynamicList) {
            if (!listFixed.contains(newList)) {
                listFixed.add(newList);
            }
        }
    
        //this is for add elements if you want eraser also 
    
        String removeRegister="";
        for (String fixedList : listFixed) {
            if (!dynamicList.contains(fixedList)) {
                removeResgister = fixedList;
            }
        }
        fixedList.remove(removeRegister);
    }
    

    这一切都是为了从一个列表更新到另一个列表,你可以做所有从一个列表和方法更新您检查列表和橡皮擦或添加列表之间的元素 . 这意味着两个列表总是大小相同

  • 0

    使用Iterator而不是Array List

    将一个集合转换为类型匹配的迭代器

    然后移动到下一个元素并删除

    Iterator<Insured> itr = insuredSet.iterator();
    while (itr.hasNext()) { 
        itr.next();
        itr.remove();
    }
    

    移到下一个是很重要的,因为它应该采用索引删除元素 .

  • 0

    只需在ArrayList.remove(A)语句后添加一个中断

相关问题