更新值时出现 ConcurrentModificationException

ConcurrentModificationException while updating values

update 函数应通过调用函数 test 来更新 HashMap 中的每个值以确定新值。 test 函数 returns 为 1 或 0,具体取决于它周围的 8 locations/neighbors。虽然,每次程序进入更新功能时我都会得到一个 ConcurrentModificationException

private static void update(){
   for(Cell e : map.keySet()){
       map.put(e,test(e.getX(),e.getY()));
  }
}

private static int test(int i, int j){
    //count alive neighbors
    int sum = 0;
    if(map.get(new Cell(i-1, j - 1)) == null){
        map.put(new Cell(i-1, j - 1), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i-1, j - 1));
    } else {
        sum += map.get(new Cell(i-1, j - 1));
    }

    if(map.get(new Cell(i, j - 1)) == null){
        map.put(new Cell(i, j - 1), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i, j - 1));
    } else {
        sum += map.get(new Cell(i, j - 1));
    }

    if(map.get(new Cell(i + 1, j - 1)) == null){
        map.put(new Cell(i + 1, j - 1), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i + 1, j - 1));
    } else {
        sum += map.get(new Cell(i + 1, j - 1));
    }

    if(map.get(new Cell(i + 1, j)) == null){
        map.put(new Cell(i + 1, j), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i + 1, j));
    } else {
        sum += map.get(new Cell(i + 1, j));
    }

    if(map.get(new Cell(i + 1, j + 1)) == null){
        map.put(new Cell(i + 1, j + 1), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i + 1, j + 1));
    } else {
        sum += map.get(new Cell(i + 1, j + 1));
    }

    if(map.get(new Cell(i, j + 1)) == null){
        map.put(new Cell(i, j + 1), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i, j + 1));
    } else {
        sum += map.get(new Cell(i, j + 1));
    }

    if(map.get(new Cell(i - 1, j + 1)) == null){
        map.put(new Cell(i - 1, j + 1), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i - 1, j + 1));
    } else {
        sum += map.get(new Cell(i - 1, j + 1));
    }

    if(map.get(new Cell(i - 1, j)) == null){
        map.put(new Cell(i - 1, j), ((Math.random()<0.5)?0:1));
        sum += map.get(new Cell(i - 1, j));
    } else {
        sum += map.get(new Cell(i - 1, j));
    }

    //return to be alive or dead
    int temp = 0;
    if(map.get(new Cell(i,j)) == 1){
        if(sum < 2){
            temp = 0;
        } else if(sum == 2 || sum == 3){
            temp = 1;
        } else if(sum > 3){
            temp = 0;
        }
    } else {
        if(sum == 3){
            temp = 1;
        }
    }
    return temp;
}

你得到 ConcurrentModificationException 是因为你在迭代 Map 的同时修改它,这是不允许的。

您可以复制当前地图以进行迭代。 不是在同一张地图上进行操作,而是遍历一张地图并对第二张地图进行操作,然后使用第二张地图作为输出。

阅读 ConcurrentModificationException 以获得更多理解。

我不想回答您的问题(已经完成),而是想帮助您改进编码。

因为您使用的是 map.get(new Cell(i-1, j-1)),我希望您已经提供了 Cell#hashcode()Cell#equals() 的实现。如果没有,请阅读 HashMap 的工作原理,然后立即实施这些方法。

考虑以下代码:

if (map.get(new Cell(i-1, j-1)) == null){
    map.put(new Cell(i-1, j-1), ((Math.random()<0.5)?0:1));
    sum += map.get(new Cell(i-1, j-1));
}
else{
    sum += map.get(new Cell(i-1, j-1));
}

在这里,你打了四次new Cell(i-1, j-1)!据推测,这些 Cell 个对象中的每一个都将彼此相等,但为什么不创建一次,并在后续调用中重用它:

Cell neighbour = new Cell(i-1, j-1);
if (map.get(neighbour) == null){
    map.put(neighbour, ((Math.random()<0.5)?0:1));
    sum += map.get(neighbour);
}
else{
    sum += map.get(neighbour);
}

垃圾收集器现在会更快乐一些。但是,我仍然看到 3 次调用 map.get(neighbour)!为什么?最后两个是相同的... sum += map.get(neighbour);if 语句的每个分支的末尾。如果我们把它移出声明,事情会变得更好!

Cell neighbour = new Cell(i-1, j-1);
if (map.get(neighbour) == null){
    map.put(neighbour, ((Math.random()<0.5)?0:1));
}
sum += map.get(neighbour);

这是一个相当大的改进。

(您可以使用 computeIfAbsent() 进一步减少代码,这将获取值,并在必要时创建它。如果需要,请随意研究它;您需要了解 lambda。)

每个相邻小区的代码都是相同的。所以您可以将该代码放入它自己的函数中并调用它。

int sum = 0;
sum += neighbour_value(i-1, j-1);
sum += neighbour_value(i,   j-1);
sum += neighbour_value(i+1, j-1);
... etc ...

希望对您有所帮助。

编码愉快。