javajavafxiteratorgridpaneobservablelist

ConcurrentModificationException while using Iterator<Node>


I was trying to delete a row of JavaFX GridPane with JavaFX Rectangles and I found no way to do so but copying the squares above to one row below. Here is my code to do that but it keeps throwing ConcurrentModificationAcception.

static void copyAbove(int rowToBeDisappear, GridPane mainGrid) {
        for (int y = (rowToBeDisappear-1); 0 <= y ; y--) {
            for (int x = 0; x <= 9; x++) {
                Iterator<Node> iterator = mainGrid.getChildren().iterator();
                while (iterator.hasNext()) {
                    Node sqr = iterator.next();
                    if (sqr == getSqrByIndex(x,y,mainGrid)) {
                        iterator.remove();
                        mainGrid.add(sqr,x,(y+1));
                    }
                }
            }
        }
    }

The error

Caused by: java.util.ConcurrentModificationException at com.sun.javafx.collections.VetoableListDecorator$VetoableIteratorDecorator.checkForComodification(VetoableListDecorator.java:714) at com.sun.javafx.collections.VetoableListDecorator$VetoableIteratorDecorator.hasNext(VetoableListDecorator.java:682) at Main.copyAbove(Main.java:%local_code_row_nvm_this%)


Solution

  • Thanks to @Slaw for pointing out the flaw in my solution.

    You can't iterate an iterator and modify its backing collection (except through that iterator's remove method) at the same time. Store any structural changes you'd like done on the collection to a temporary collection, then perform them after iterating.

    If getSqrByIndex() is guaranteed to return at most one Node given an x and a y, then the following code will not cause CMEs:

    Node node = null;
    
    Iterator<Node> iterator = mainGrid.getChildren().iterator();
    while (iterator.hasNext()) {
        Node sqr = iterator.next();
        if (sqr == getSqrByIndex(x,y,mainGrid)) {
            node = sqr;
        }
    }
    
    if (node != null) {
        mainGrid.getChildren().remove(node);
        mainGrid.add(node, x, y + 1);
    }