在Java中为具有循环引用的对象实现equals和hashCode

Tom*_*Tom 33 java equals hashcode

我定义了两个类,它们都包含对另一个对象的引用.它们看起来与此类似(这是简化的;在我的真实域模型中,A类包含B的列表,每个B都有一个返回父A的引用):

public class A {

    public B b;
    public String bKey;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((b == null) ? 0 : b.hashCode());
        result = prime * result + ((bKey == null) ? 0 : bKey.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof A))
            return false;
        A other = (A) obj;
        if (b == null) {
            if (other.b != null)
                return false;
        } else if (!b.equals(other.b))
            return false;
        if (bKey == null) {
            if (other.bKey != null)
                return false;
        } else if (!bKey.equals(other.bKey))
            return false;
        return true;
    }
}

public class B {

    public A a;
    public String aKey;

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((a == null) ? 0 : a.hashCode());
        result = prime * result + ((aKey == null) ? 0 : aKey.hashCode());
        return result;
    }
    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (!(obj instanceof B))
            return false;
        B other = (B) obj;
        if (a == null) {
            if (other.a != null)
                return false;
        } else if (!a.equals(other.a))
            return false;
        if (aKey == null) {
            if (other.aKey != null)
                return false;
        } else if (!aKey.equals(other.aKey))
            return false;
        return true;
    }
}
Run Code Online (Sandbox Code Playgroud)

hashCodeequals已通过使用Eclipse中A和B这两个问题的两个场产生的是调用equalshashCode在任一对象方法的结果在StackOverflowError因为它们都调用另一个对象的equalshashCode方法.例如,StackOverflowError使用上述对象时,以下程序将失败:

    public static void main(String[] args) {

        A a = new A();
        B b = new B();
        a.b = b;
        b.a = a;

        A a1 = new A();
        B b1 = new B();
        a1.b = b1;
        b1.a = a1;

        System.out.println(a.equals(a1));
    }
Run Code Online (Sandbox Code Playgroud)

如果以这种方式使用循环关系定义域模型存在固有的错误,请告诉我.据我所知虽然这是一个相当常见的情况,对吗?

定义hashCodeequals在这种情况下,最佳做法是什么?我想保留equals方法中的所有字段,以便它是对象的真正深度相等比较,但我不知道如何解决这个问题.谢谢!

tot*_*to2 5

我同意I82Much的,你应该避免乙引用它们的父注释:这是信息重复,这通常只会导致麻烦,但你可能需要在你的情况下这样做.

即使你离开父参考B,尽可能的散列码而言,你应该完全忽略父参考,并只使用真正的内部变量B来构建哈希码.

As为只是容器,其价值是由它们的内容,这是包含的值完全确定B,依次类推应他们的哈希键.

如果A是无序集,则必须非常小心,您根据B值(或B哈希码)构建的哈希代码不依赖于某些排序.例如,如果通过B在某个序列中添加和乘以所包含的哈希码来构建哈希码,则应首先在计算求和/乘法的结果之前通过递增顺序来对哈希码进行排序.同样,A.equals(o)不得依赖于Bs 的排序(如果是无序集).

请注意,如果您使用的是java.util.CollectionA,那么就固定B忽略父参考会自动给出有效的S哈希码A的散列码,因为CollectionS按默认(订货与否)具有良好的散列码.