当我将一个元素添加到 java HashSet 时,所有元素都会发生变化

When I add an element to a java HashSet, all the elements change

在 Conway 的生命游戏模拟器中,我有八个看起来像这样的代码块来查找单元格的邻域。

int count = 0;
    p.setLocation(p.x-1, p.y-1); //upper left
    if( data.contains(p) ) ++count;
    else if( addingDeadCells ) {
        deadCellsToCheck.add(p);
        for( Point z : deadCellsToCheck ) 
            System.out.println(z.toString() + " " + z.hashCode()); 
        System.out.println();
    }

p是代表当前单元格的点,data是包含活跃单元格的HashSet。为了提高效率,我只检查与活细胞相邻的死细胞,因此 deadCellsToCheck 是另一个 HashSet,它在每一代开始时都是空的。每次我执行 deadCellsToCheck.add(p) 时,似乎所有已经在其中的单元格都被覆盖为刚刚添加的单元格,因为输出如下所示:

java.awt.Point[x=0,y=0] 0

java.awt.Point[x=1,y=0] 1072693248
java.awt.Point[x=1,y=0] 1072693248

java.awt.Point[x=2,y=0] 1073741824
java.awt.Point[x=2,y=0] 1073741824
java.awt.Point[x=2,y=0] 1073741824
etc...

出于多种原因,我认为这不可能。有什么想法吗?

这个问题的原因是您的 HashSet 多次包含相同的 Point 实例。您应该创建一个新点并将其添加到集合中,而不是编辑点 p.setLocation(p.x-1, p.y-1);

int count = 0;
p = new Point(p.x-1, p.y-1); // new point here
if (data.contains(p)) {
     count++;
} else if (addingDeadCells) {
    deadCellsToCheck.add(p);
    for (Point z : deadCellsToCheck)  {
        System.out.println(z.toString() + " " + z.hashCode());
    }
    System.out.println();
}

您当前的结构也违反了 hashCode() 方法的约定:

Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer

集合使用hashCodeequals方法来消除重复。您创建了具有 hashcode = 0 的点 (0,0)。然后将点修改为 (1,0),它的 haschode 更改为 1072693248 因此集合允许再次插入该点。但它是同一个实例 - 它是同一个对象两次插入集合中。

通常不应该在集合或依赖于 hashcode 方法的其他数据结构中使用可变对象。

解决方案是使点不可变(如果您使用 java.awt.Point,我建议您创建自己的 class):

public class Point {

     private final int x;
     private final int y;

     public Point(int x, int y) {
         this.x = x;
         this.y = y;
     }

     // hashcode and equals methods
     // getters and utility methods

}

看起来你在一遍又一遍地向集合中添加一个点,并改变它的值。如果你想添加一堆不同的点,你需要制作一堆新的点对象(使用单词new)而不是一个。