提问者:小点点

java.util.HashSet是否不遵守其规范?


作为一个相对的Java新手,我很困惑地发现了以下几点:

点. java:

public class Point {
...
    public boolean equals(Point other) {
        return x == other.x && y == other.y;
    }
...
}

Edge.java:

public class Edge {
    public final Point a, b;
    ...
    public boolean equals(Edge other) {
        return a.equals(other.a) && b.equals(other.b);
    }
...
}

主代码段:私有集blockedEdges

public Program(...) {
    ...
    blockedEdges = new HashSet<Edge>();

    for (int i = 0; ...) {
        for (int j = 0; ...) {

            Point p = new Point(i, j);              
            for (Point q : p.neighbours()) {

                Edge e = new Edge(p, q);
                Edge f = new Edge(p, q);

                blockedEdges.add(e);


                // output for each line is: 
                // contains e? true; e equals f? true; contains f? false

                System.out.println("blocked edge from "+p+"to " + q+
                      "; contains e? " + blockedEdges.contains(e)+
                      " e equals f? "+ f.equals(e) + 
                      "; contains f? " + blockedEdges.contains(f));
            }
        }
    }
}

为什么这令人惊讶?因为在我将其编码为依赖平等之前,我检查了留档,它说:

如果该集合包含指定的元素,则返回true。更正式地说,当且仅当这个集合包含元素e,使得(o==null?e==null : o.equals(e))

这句话非常清楚,它指出只需要相等。f.equals(e)返回true,如输出所示。所以很明显,集合确实包含一个元素e,o.equals(e),但包含(o)返回false。

虽然可以理解散列集也依赖于相同的散列值,但是在散列集本身的文档中没有提到这个事实,在集的文档中也没有提到任何这样的可能性。

因此,HashSet不符合其规范。这对我来说是一个非常严重的错误。我是不是完全走错了方向?或者这样的行为是如何被接受的?


共1个答案

匿名用户

您没有重写< code>equals(您正在重载它)。< code>equals需要接受< code>Object作为参数。

做一些像

@Override
public boolean equals(Object o) {
    if (!(o instanceof Point))
        return false;
    Point other = (Point) o;
    return x == other.x && y == other.y;
}

边缘也一样

当您覆盖equals时,始终覆盖hashCode也很重要。例如,请参阅为什么我需要覆盖Java中的equals和hashCode方法?

请注意,如果您使用了@Override,则编译会捕获此错误。这就是为什么在可能的情况下始终使用它是一种很好的做法。