如何计算此类的hashCode

时间:2019-04-16 11:16:33

标签: java hashcode

我正在尝试计算一个类的哈希码,但出现了stackoverflow。如何正确执行此操作?我是通过IntelliJ想法生成的,但还是如此。有stackoverflow,我知道原因(可能),但是我真的想计算适当的哈希码。.

public class Main {
    public static void main(String[] args) {
        TestA testA = new TestA();
        TestB testB = new TestB();
        testA.id = 1;
        testA.name = "test";
        testA.testB = testB;
        testB.testA = testA;
        testB.id = 1;
        testB.name = "test";



        System.out.println(testA.hashCode());
    }
}

class TestB {
    int id;
    String name;
    TestA testA;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof TestB)) return false;

        TestB testB = (TestB) o;

        if (id != testB.id) return false;
        if (name != null ? !name.equals(testB.name) : testB.name != null) return false;
        return testA != null ? testA.equals(testB.testA) : testB.testA == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (testA != null ? testA.hashCode() : 0);
        return result;
    }
}


class TestA {
    int id;
    String name;
    TestB testB;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof TestA)) return false;

        TestA testA = (TestA) o;

        if (id != testA.id) return false;
        if (name != null ? !name.equals(testA.name) : testA.name != null) return false;
        return testB != null ? testB.equals(testA.testB) : testA.testB == null;
    }

    @Override
    public int hashCode() {
        int result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + (testB != null ? testB.hashCode() : 0);
        return result;
    }
}

我也包含了主要功能。您可以轻松打开它。.

1 个答案:

答案 0 :(得分:1)

您正在寻找的是一种在不进入无限循环的情况下遍历对象树的方法。这可以通过以下方式来实现:将访问的对象存储在 thread-local Set中,并在hashcode位于该集合中时输入this时停止。

您不能随便用HashSet来存储'visited'对象,因为它在内部调用了hashcode,因此问题只是转移到其他地方,您仍然得到堆栈溢出。幸运的是,有一个使用标识而不是相等的容器,但是它是Map的变体,而不是Set。理想情况下,您想要IdentityHashSet,但它不存在,但是仍然有用的IdentityHashMap存在。只需将键用作实际内容并使用伪值即可。

public class Main {
    public static void main(String[] args) {
        TestA testA = new TestA();
        TestB testB = new TestB();
        testA.id = 1;
        testA.name = "test";
        testA.testB = testB;
        testB.testA = testA;
        testB.id = 1;
        testB.name = "test";

        System.out.println(testA.hashCode());
    }
}

class TestB {
    int    id;
    String name;
    TestA  testA;

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof TestB))
            return false;

        TestB testB = (TestB)o;

        if (id != testB.id)
            return false;
        if (name != null ? !name.equals(testB.name) : testB.name != null)
            return false;
        return testA != null ? testA.equals(testB.testA) : testB.testA == null;
    }

    private static final ThreadLocal<Set<Object>> VISITED = ThreadLocal.withInitial(() -> new HashSet(10));

    @Override
    public int hashCode() {
        Set<Object> visited = VISITED.get();
        if (visited.contains(this))
            return 0;

        visited.add(this);
        try {
            int result = id;
            result = 31 * result + (name != null ? name.hashCode() : 0);
            result = 31 * result + (testA != null ? testA.hashCode() : 0);
            return result;
        } finally {
            visited.remove(this);
        }
    }
}

class TestA {
    int    id;
    String name;
    TestB  testB;

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (!(o instanceof TestA))
            return false;

        TestA testA = (TestA)o;

        if (id != testA.id)
            return false;
        if (name != null ? !name.equals(testA.name) : testA.name != null)
            return false;
        return testB != null ? testB.equals(testA.testB) : testA.testB == null;
    }

    private static final ThreadLocal<Map<Object, Object>> VISITED =
            ThreadLocal.withInitial(() -> new IdentityHashMap<>(10));

    @Override
    public int hashCode() {
        Map<Object, Object> visited = VISITED.get();
        if (visited.containsKey(this))
            return 0;

        visited.put(this, this);
        try {
            int result = id;
            result = 31 * result + (name != null ? name.hashCode() : 0);
            result = 31 * result + (testB != null ? testB.hashCode() : 0);
            return result;
        } finally {
            visited.remove(this);
        }
    }
}

注意:两个VISITED变量可以是一个变量,但是由于您的类没有一个公共的超类(Object除外),因此我必须将它们做成两个。

注意事项::当树包含同一类的同一实例多次时,该实例的哈希码将被多次计算。这是因为每次该实例完成访问后,它就会从列表中删除。这是因为您不希望对这些实例的硬引用保留在线程本地的Map中,以防止垃圾回收。