【问题标题】:Concurrent modification exception when using iterator? (Java)使用迭代器时并发修改异常? (爪哇)
【发布时间】:2015-09-03 04:18:48
【问题描述】:

我正在尝试为鲨鱼和鱼互相吃食并模拟人口控制等的模拟 WaTor 编写代码。无论如何,我遇到的问题是,即使使用迭代器,我也会不断收到并发修改异常。

这是我的代码:

private void updateSharks() {   
    for(Iterator<Shark> sharkit = sharks.iterator(); sharkit.hasNext();) {
        Shark sharky = sharkit.next();
        if(sharky.hasStarved()) {
            sharkit.remove();
        }
        else if(sharky.canBreed()) {
            addNewShark(sharky.getX(),sharky.getY());
        }
        moveShark(sharky);
    }
}

这里是个例外:

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at WaTor.Ocean.updateSharks(Ocean.java:281)
at WaTor.Ocean.update(Ocean.java:307)
at WaTor.Main.main(Main.java:13)

海洋中的第 281 行是“Shark Sharky = Sharkit.next();”感谢您的帮助!

【问题讨论】:

  • 使用迭代器遍历集合时不能修改集合。例如,检查此答案以了解实现您想要的方法:stackoverflow.com/q/122105/4250114
  • 我猜addNewSharkmoveShark 应该归咎于此;他们可能以不安全的方式访问收藏。
  • 另外,关于你的堆栈跟踪:它抛出的原因是因为Iterator.next 是它检查商品化的时候(你可以从跟踪中的方法中猜到)。它不会发生在您实际对集合进行商品化的时候。

标签: java debugging arraylist iterator


【解决方案1】:

您不能在遍历列表时对其进行修改。 如果您在遍历时从列表中删除元素,那么 - 修复它的一个选项是将这些要删除的元素放在另一个列表中,然后遍历该列表以删除元素。 - 其他选项是使用 Iterator.remove() 您正在使用 Iterator.remove()。所以没关系。

如果您在遍历时添加元素,这也可能是一个问题。我没有测试你的代码。但请确保您没有在 addNewSharks() 和 moveSharks() 方法中添加元素以列出鲨鱼

【讨论】:

    【解决方案2】:

    正如其他人提到的,您不能在迭代时修改集合(删除是可选的)。好吧,您可以将所有应该死的鲨鱼收集到另一个集合中,并在迭代器循环终止后将它们删除。

    尝试以下方法:

    private void updateSharks() { 
        ArrayList<Shark> toRemove = new ArrayList<Shark>();
        ArrayList<Shark> toAdd = new ArrayList<Shark>();
    
        for(Iterator<Shark> sharkit = sharks.iterator(); sharkit.hasNext();) {
            Shark sharky = sharkit.next();
            if(sharky.hasStarved()) {
                toRemove.add(sharky);
            }
            else if(sharky.canBreed()) {
                toAdd.add(/*create new shark object here*/)
            }
            moveShark(sharky);
        }
    
        for (Shark shark : toRemove) {
            sharks.remove(shark);
        }
        for (Shark shark: toAdd) {
            sharks.add(shark);
        }
    }
    

    编辑:它是如何工作的

    您正在遍历集合。在这一步,你可以在集合中包含的对象中做任何你想做的事情,但你不能改变集合本身,因为迭代器不允许你这样做。

    因此,您创建了第二个临时集合。每次遇到要从主集合中删除的对象时,都会将此对象添加到临时集合中。因为在Java中只传递引用,这里没有深拷贝,是两个集合中同时包含的同一个对象。

    现在,当迭代结束时,您的临时集合中包含了所有对象(或它们的引用,如果您愿意这样考虑的话)。因为迭代器消失了,没有什么能阻止你从主集合中删除所有这些对象,而且很容易做到!

    【讨论】:

    • 即使在移动后,toRemove 是否仍会识别我添加到其中的鲨鱼?移动鲨鱼只是改变了它的 x 和 y 坐标,但这是否意味着鲨鱼中的鲨鱼与 toRemove 中的鲨鱼不同?
    • 不,它仍然是同一个鲨鱼。这是因为它是对同一个对象的引用。
    • 这行得通!谢谢。如果你不介意,我还是不太明白它是如何工作的。所以我在 toRemove 中添加了鲨鱼,它指的是饥饿的鲨鱼。然后我遍历我的鲨鱼列表并移动鲨鱼。这是否意味着 toRemove 中的鲨鱼也被移动了,因为它指的是同一条鲨鱼?
    • 很酷,谢谢。现在我只需要处理传送鲨鱼和鱼哈哈
    • 传送到底是什么意思 ;-) ?
    【解决方案3】:

    请查看Iterator 的JavaDoc。对于remove 方法,您可以找到以下文本:

    从底层集合中移除此迭代器返回的最后一个元素(可选操作)。每次调用 next() 时只能调用一次此方法。如果在迭代过程中以任何方式而不是调用 ths 方法修改了底层集合,则迭代器的行为是未指定的。

    在我看来,您似乎是在迭代集合时向集合中添加元素。因此,您可能会遇到未指明的行为。

    【讨论】:

      【解决方案4】:

      不要使用迭代器。像这样向后遍历集合:

      import java.util.ArrayList;
      
      public class RemoveFromCollectionExample {
      
          //
          // instance variables
          //
      
          private ArrayList<Shark> sharks;
      
          //
          // main
          //
      
          public static void main(String[] args) {
              RemoveFromCollectionExample example = new RemoveFromCollectionExample();
              example.sharks = new ArrayList<RemoveFromCollectionExample.Shark>();
              example.addShark(false, false, 0, 0);
              example.addShark(false, false, 0, 0);
              example.addShark(false, false, 0, 0);
              example.addShark(false, false, 0, 0);
              example.addShark(false, false, 0, 0);
              example.addShark(false, false, 0, 0);
              example.addShark(false, true, 0, 0);
              example.addShark(true, false, 0, 0);
              example.addShark(true, false, 0, 0);
              example.addShark(true, false, 0, 0);
              System.out.println("START WITH: " + example.sharks.size());
              example.updateSharks();
              System.out.println("Added 1, Removed 3");
              System.out.println("ENDED WITH: " + example.sharks.size());
          }
      
          //
          // update sharks
          //
      
          private void updateSharks() {
              for (int i = this.sharks.size() - 1; i > 0; i--) {
                  Shark sharky = this.sharks.get(i);
                  if (sharky.hasStarved()) {
                      this.sharks.remove(i);
                      System.out.println("REMOVED");
                  } else if (sharky.canBreed()) {
                      addNewShark(sharky.getX(), sharky.getY());
                      System.out.println("ADDED");
                  }
                  moveShark(sharky);
              }
          }
      
          //
          // add new shark method
          //
      
          private void addNewShark(int x, int y) {
              this.sharks.add(new Shark(false, false, x, y));
          }
      
          private void addShark(boolean hasStarved, boolean canBreed, int x, int y) {
              this.sharks.add(new Shark(hasStarved, canBreed, x, y));
          }
      
          //
          // move method
          //
      
          private void moveShark(Shark sharky) {
          }
      
          //
          // Shark class
          //
      
          private class Shark {
      
              private boolean hasStarved;
              private boolean canBreed;
              private int x;
              private int y;
      
              public boolean hasStarved() {
                  return hasStarved;
              }
      
              public void setHasStarved(boolean hasStarved) {
                  this.hasStarved = hasStarved;
              }
      
              public boolean canBreed() {
                  return canBreed;
              }
      
              public void setCanBreed(boolean canBreed) {
                  this.canBreed = canBreed;
              }
      
              public int getX() {
                  return x;
              }
      
              public void setX(int x) {
                  this.x = x;
              }
      
              public int getY() {
                  return y;
              }
      
              public void setY(int y) {
                  this.y = y;
              }
      
              public Shark(boolean hasStarved, boolean canBreed, int x, int y) {
                  super();
                  this.hasStarved = hasStarved;
                  this.canBreed = canBreed;
                  this.x = x;
                  this.y = y;
              }
      
              public void moveShark() {
              }
      
          }
      
      }
      

      【讨论】:

        猜你喜欢
        • 2011-01-16
        • 1970-01-01
        • 1970-01-01
        • 2014-09-20
        • 1970-01-01
        • 2018-10-21
        • 1970-01-01
        • 2014-10-08
        • 1970-01-01
        相关资源
        最近更新 更多