如何检测List是否包含Java本身

时间:2015-06-24 21:48:23

标签: java list recursion identity hashcode

来自Java documentation

注意:虽然列表允许将自己包含为元素,但建议极其谨慎: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(对象)并对其进行测试是否足够好?

1 个答案:

答案 0 :(得分:4)

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个不同的identityHashCode可能...

如果它不仅仅是Iterable中的成员身份,而是任何的任何循环引用,那么变得更难,尽管可以用反射来实现。也就是说,这种循环引用的存在并不一定意味着equalshashCode不起作用;只要equalshashCode方法中的引用是非循环的,并且没有通用的方法来检测它,那么循环引用是完全可以的。