【问题标题】:How to add values to a list while iterating it [duplicate]如何在迭代列表时将值添加到列表中[重复]
【发布时间】:2014-07-03 16:33:24
【问题描述】:

我有这样的场景

List<String> xxx = new ArrayList
for(String yyy : xxx){
   for(String zzz:xyxy){
     if(!zzz.equals(yyy)){
       xxx.add(zzz);
     }
   }
}

但我得到 java.util.ConcurrentModificationException: null 异常。谁能帮我解决这个问题。?谁能给我替代方法来执行此操作?

【问题讨论】:

  • 你到底想做什么?
  • 顺便说一句,您需要提高变量命名技巧。仅由于非描述性名称,您的示例代码很难理解。

标签: java concurrentmodification


【解决方案1】:

看着ArrayList API

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

因此,您需要明确获取ListIterator 并使用它来正确更改您的ArrayList。另外,我不相信您可以使用 for-each 循环,因为其中的迭代器与您显式检索的迭代器是分开的。我认为您必须使用 while 循环:

List<String> xxx = new ArrayList<>();
ListIterator<String> iterator = xxx.listIterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    for (String zzz : xyxy) {
        if (!zzz.equals(s)) {
            iterator.add(zzz); //<-- Adding is done through the iterator
        }
    }
}

【讨论】:

  • 哟,谢谢兄弟..它像宝石一样工作......!
  • 上面每个循环的 xyxy 是什么?
  • @Rajper 我想这是某种Iterable&lt;String&gt;。具体是什么品种,不知道。它在最初的问题中,所以我就这样离开了。
  • 是的,它肯定是某种字符串集合,但我想问的是什么;它必须在 sn-p 中的某处声明或初始化。
  • @Rajper 我认为丢失的声明只是被遗忘了。这不是问题的重要细节。
【解决方案2】:

将它们添加到另一个列表中,然后将该列表添加到xxx

List<String> xxx = new ArrayList<>();
List<String> additional = new ArrayList<>();
for (String yyy : xxx) {
    for (String zzz : xyxy) {
        if (!zzz.equals(yyy)) {
            additional.add(zzz);
        }
    }
}
xxx.addAll(additional);

【讨论】:

    【解决方案3】:

    要在迭代时修改列表,您可以将数组列表声明为“java.util.concurrent.CopyOnWriteArrayList

    public static void main(String[] args) {
        List<String> xxx = new CopyOnWriteArrayList();
        xxx.add("3");
        xxx.add("4");
    
        List<String> xyxy = new ArrayList();
        xyxy.add("1");
        xyxy.add("2");
        xyxy.add("3");
    
        for (String yyy : xxx) {
            for (String zzz : xyxy) {
                if (!zzz.equals(yyy)) {
                    xxx.add(zzz);
                }
            }
        }
    
        System.out.println(xxx);
    }
    

    【讨论】:

    • 但是,根据在线 API,“迭代器不会反映自创建迭代器以来对列表的添加、删除或更改”。这可能是有问题的,具体取决于 OP 想要什么。此外,CopyOnWriteArrayLists 的性能会很糟糕,如果您的读取次数不超过写入次数
    • 同意。 CopyOnWriteArrayList 将在修改次数与遍历操作数相比最少时有效。
    • 这里好像不是这样...
    【解决方案4】:

    不要使用 foreach 循环,而是使用普通循环,例如:

    int originalSize=xxx.size();
    for (int i=0;i<originalSize;i++) {
        xxx.add("New string appended at the end");
    }
    

    这允许您在列表中存在的项目在开始迭代之前进行迭代,如果您对列表的唯一操作是添加一些新项目在它的末尾。

    【讨论】:

    • 不要这样做! xxx.size() 正在更新,而 originalSize 保持不变。
    • @ginz 这是故意的!仅对列表中最初包含的项目进行迭代(而不是在循环期间添加的新项目中)
    • 这与作者代码中的行为不同。
    • OP 的意图并没有真正指定。但是,如果您通过检查 xxx.size() 设置结束循环的条件,您就有可能出现无限循环(如果您继续添加新项目)。
    【解决方案5】:

    这意味着您不能同时访问和修改您的收藏(列表)。为此,您必须使用迭代器。我不明白您的代码中的 xyxy 是什么。但是元素的可能迭代和添加可能看起来像这样。

            List<String> xxx = new ArrayList<String>();
    
            xxx.add("hello");
            xxx.add("hi");
            xxx.add("hh");
    
            ListIterator<String> it = xxx.listIterator();
            while (it.hasNext()) {
    
                if (!it.next().equals("hi")) {
                    it.remove();
                    it.add("hi");
    
                }
            }
    
            System.out.println(xxx);
              }
          }
    

    【讨论】:

    • 我相信这仍然会抛出ConcurrentModificationException,因为您仍在以不通过迭代器本身的方式修改ArrayList
    • 感谢指出,编辑代码
    猜你喜欢
    • 2011-11-16
    • 1970-01-01
    • 2011-01-23
    • 1970-01-01
    • 1970-01-01
    • 2019-10-06
    • 2023-04-02
    • 2012-07-22
    • 1970-01-01
    相关资源
    最近更新 更多