注意:虽然允许列表将自己包含为元素,但建议极其谨慎:equals和hashCode方法不再在这样的列表中很好地定义.
问题是List对象的哈希码是递归计算的.
int hashCode = 1;
for (E e : list)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
问题是如何使我的代码傻瓜证明并检测List对象(或其某些项目甚至更深)是否包含List对象本身.
如何在遍历List对象时保留List对象列表并能够调用contains() – 类似方法?保持System.identityHashCode(对象)并对它进行测试足够好吗?
解决方法:
System.identityHashCode会有所帮助,但使用其中一个内置工具按身份跟踪对象几乎肯定会更简单 – IdentityHashMap.
boolean containsCircularReference(Iterable<?> iterable) {
return containsCircularReference(
iterable,
Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>()));
}
private boolean containsCircularReference(Object o, Set<Object> seen) {
if (seen.contains(o)) {
return true;
}
seen.add(o);
if (o instanceof Iterable) {
for (Object o2 : (Iterable<?>) o) {
if (containsCircularReference(o2, seen)) {
return true;
}
}
}
return false;
}
作为参考,您不能依赖于System.identityHashCode是无冲突的.对于初学者,您可以在JVM中分配超过2 ^ 32个对象,并且只有2 ^ 32个不同的identityHashCodes可能…
如果它不仅仅是Iterable的成员资格,而是任何循环引用,那就更难了,尽管可以用反射来实现.也就是说,这种循环引用的存在并不一定意味着equals和hashCode不起作用;只要equals和hashCode方法中的引用是非循环的,并且没有通用的方法来检测它,那么循环引用是完全可以的.