Thomas Ayling
Thomas Ayling

Reputation: 95

ConcurrentModificationException in Processing

I'm trying to implement an ArrayList into something I'm making but whenever I to add a new item into the ArrayList from within a loop, i get a ConcurrentModificationError, which makes sense yet i don't know how to get around this.

My code looks like this:

ArrayList<Pipe> pipes;

void setup() {
  size(640, 360);
  pipes = new ArrayList<Pipe>();
  make();
}
void make() {
  pipes.add(new Pipe());
  Pipe P = pipes.get((pipes.size()-1));
  P.create();
}

void draw() {
  background(255);
  for (Pipe p : pipes) {
    p.display();
    p.move();
    if (p.x < 3*width/4) {
      make();
      println("A");
    }  
  }
}

The error is definitely not coming from the object it's self and the error occurs when the make() funtion is called within draw().

Any help would be much appreciated, thanks.

Upvotes: 1

Views: 943

Answers (2)

Vasan
Vasan

Reputation: 4956

Use a ListIterator to do the iteration and adding to list.

for (ListIterator<Pipe> iter = pipes.listIterator(); iter.hasNext();) {
    Pipe p = iter.next();
    [...]
    make(iter);
}

And then in make:

iter.add(new Pipe());

From the javadoc for ListIterator:

An iterator for lists that allows the programmer to traverse the list in either direction, modify the list during iteration, and obtain the iterator's current position in the list.

Instead of adding to the list, you would be adding to the iterator. There is no concurrent modification in this case since the iterator is able to keep track of list changes.

Note that the iter.add() call would add the element just before the element returned by iter.next(). This would mean that the newly added object would NOT be the last in the list. This is generally preferable because you don't want to disturb the loop's forward flow i.e. you don't want the next iteration of the loop to be on the newly added object. But your case may vary.

Upvotes: 1

Michael Krause
Michael Krause

Reputation: 4859

I suggest using a CopyOnWriteArrayList for this.

This will allow mutations to the list while retaining the integrity of the iterator by causing a copy of the underlying array to be made.

This, of course, comes at a performance cost.

Give this a try (I stubbed out methods you call for which you didn't provide an implementation):

import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

public class Test {
    private int width;

    private class Pipe {
        public int x;
        public void display() {
        }
        public void move() {
        }
        public void create() {
        }
    }

    List<Pipe> pipes;

    void size(int w, int h) {
    }

    public void background(int i) {
    }

    void setup() {
        size(640, 360);
        pipes = new CopyOnWriteArrayList<Pipe>();
        make();
    }

    void make() {
        pipes.add(new Pipe());
        Pipe P = pipes.get((pipes.size() - 1));
        P.create();
    }

    void draw() {
        background(255);
        for (Pipe p : pipes) {
            p.display();
            p.move();
            if (p.x < 3 * width / 4) {
                make();
                System.out.println("A");
            }
        }
    }

    public static void main(String[] args) {
        Test t = new Test();
        t.setup();
        t.draw();
    }
}

Upvotes: 1

Related Questions