在Java中为具有循环引用的对象实现equals和hashCode [英] Implementing equals and hashCode for objects with circular references in Java

查看:112
本文介绍了在Java中为具有循环引用的对象实现equals和hashCode的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have two classes defined such that they both contain references to the other object. They look similar to this (this is simplified; in my real domain model class A contains a list of B and each B has a reference back to parent 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;
    }
}

hashCode equals 。问题是调用等于 hashCode 方法导致 StackOverflowError ,因为它们都调用另一个对象的等于 hashCode 方法。例如,以下程序将使用上述对象以 StackOverflowError 失败:

The hashCode and equals have been generated by Eclipse using both fields of both A and B. The problem is that calling the equals or hashCode method on either object results in a StackOverflowError since they both call the other object's equals and hashCode method. For example the following program will fail with StackOverflowError using the above objects:

    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));
    }

如果在使用循环关系定义域模型时存在固有错误这样请告诉我。虽然我知道这是一个相当普遍的情况,但是对吗?

If there is something inherently wrong with having a domain model defined with circular relationships in this way then please let me know. As far as I can tell though this is a fairly common scenario, correct?

定义 hashCode 和等于?我想保留 equals 方法中的所有字段,以便它是对象的真正深度相等比较,但我不知道如何解决这个问题。谢谢!

What is best practice for defining hashCode and equals in this case? I want to keep all fields in the equals method so that it is a true deep equality comparison on the object but I don't see how I can with this problem. Thanks!

推荐答案

我同意I82的评论,你应该避免B引用他们的父母:这是信息重复,通常是只会导致麻烦,但您可能需要这样做。

I agree with the comment of I82Much that you should avoid having B referencing their parent: it's information duplication, which usually only leads to trouble, but you might need to do so in your case.

即使您将父参考留在 B ,就哈希码而言,你应该完全忽略父引用,只使用 B true 内部变量来构建哈希码。

Even if you leave the parent reference in B, as far as hash codes are concerned you should completely ignore the parent reference and only use the true inner variables of B to build the hash code.

A 只是容器,它们的值完全由它们的内容决定,这是它们的值。包含 B s,他们的哈希键也应如此。

The As are just containers and their value is fully determined by their content, which is the values of the contained Bs, and so should their hash keys.

如果 A 是一个无序集合,你必须非常小心你从 B 值构建的哈希码(或 B 哈希码)不依赖于某些排序。例如,如果通过在某个序列中添加和乘以包含的 B 的哈希码来构建哈希码,则应首先通过增加顺序来排序哈希码计算和/乘的结果。同样, A.equals(o)不得依赖于 B 的排序(如果是无序集)。

If A is an unordered set, you must be very careful that the hash code you are building from the B values (or B hash codes) is not dependent on some ordering. For example, if the hash code is build by adding and multiplying the hash codes of the contained B's in some sequence, you should first order the hash codes by increasing order before computing the result of the sums/multiplications. Similarly, A.equals(o) must not depend on the ordering of the Bs (if unordered set).

请注意,如果您在 A A 中使用 java.util.Collection code>,然后通过忽略父引用来修复 B 的哈希代码将自动给出有效的 A 哈希码,因为集合默认情况下具有良好的哈希码(订购或不订购)。

Note that if you are using a java.util.Collection within A, then just fixing the Bs hash code by ignoring the parent reference will automatically give valid A hash codes since the Collections have good hash codes by default (ordering or not).

这篇关于在Java中为具有循环引用的对象实现equals和hashCode的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆