我正在尝试计算一个类的哈希码,但我得到了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;
}
}
我也包括主要功能。你可以轻松打开这个..
您正在寻找的是一种在不进入无限循环的情况下遍历对象树的方法。这可以通过将访问对象存储在线程本地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
),我必须制作其中两个。
警告:当树包含多次相同的类实例时,该实例的哈希码将被多次计算。这是因为每次该实例访问时,它都会从列表中删除。这是因为您不希望对这些实例的硬引用保留在线程局部映射中,从而防止垃圾回收。