如何计算这个类的hashCode [重复]

问题描述 投票:0回答:1

我正在尝试计算一个类的哈希码,但我得到了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;
    }
}

我也包括主要功能。你可以轻松打开这个..

java hashcode
1个回答
1
投票

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

而且你不能只是不情愿地使用HashSet存储'访问'对象,因为它在内部调用你的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),我必须制作其中两个。

警告:当树包含多次相同的类实例时,该实例的哈希码将被多次计算。这是因为每次该实例访问时,它都会从列表中删除。这是因为您不希望对这些实例的硬引用保留在线程局部映射中,从而防止垃圾回收。

© www.soinside.com 2019 - 2024. All rights reserved.