Java 将自身作为元素包含的ArrayList的哈希代码

Java 将自身作为元素包含的ArrayList的哈希代码,java,java-8,stack-overflow,hashcode,Java,Java 8,Stack Overflow,Hashcode,我们能否找到一个列表的hashcode,该列表本身包含元素 我知道这是一种不好的做法,但这是面试官问的 当我运行以下代码时,它抛出一个StackOverflowError: public class Main { public static void main(String args[]) { ArrayList<ArrayList> a = new ArrayList(); a.add(a); a.hashCode();

我们能否找到一个
列表的
hashcode
,该列表本身包含
元素

我知道这是一种不好的做法,但这是面试官问的

当我运行以下代码时,它抛出一个
StackOverflowError

public class Main {
    public static void main(String args[]) {
        ArrayList<ArrayList> a = new ArrayList();
        a.add(a);
        a.hashCode();
    }
}
公共类主{
公共静态void main(字符串参数[]){
ArrayList a=新的ArrayList();
a、 添加(a);
a、 hashCode();
}
}
现在我有两个问题:

  • 为什么会出现堆栈溢出错误
  • 可以用这种方式找到散列码吗

  • 查看
    AbstractList
    类中
    hashCode
    方法的框架实现

    public int hashCode() {
        int hashCode = 1;
        for (E e : this)
            hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
        return hashCode;
    }
    

    对于列表中的每个元素,这将调用
    hashCode
    。在您的案例列表中,它是唯一的元素。现在这个电话永远不会结束。该方法以递归方式调用自身,并且递归会一直缠绕,直到遇到
    StackOverflowerError
    。所以你不能用这种方法找到hashCode。

    因为当你用同一个函数调用同一个函数时,它将创建一个永不结束的递归条件。为了防止此操作,JAVA将返回
    JAVA.lang.StackOverflowerError

    下面是解释类似场景的示例代码:

    public class RefTest {
    
        public static void main(String... strings) {
            RefTest rf = new RefTest();
            rf.message(message("test"));
        }
    
        public static String message2(String s){
            return message(s);
        }
    
        public static String message(String s){
            return message2(s);
        }
    
    }   
    

    拉文德拉的回答很好地解释了第一点。就问题2发表评论:

    有可能用这种方式找到散列码吗

    这里有些东西是圆形的。在该堆栈溢出错误的上下文中,这两个错误中至少有一个是错误的:

    • 列表的哈希代码必须考虑其元素的哈希代码
    • 列表作为它自己的元素是可以的
    现在,因为我们正在处理一个
    ArrayList
    ,所以第一点是固定的。换句话说,也许您需要一个不同的实现来有意义地计算递归列表的哈希代码。。。可以扩展
    ArrayList
    并跳过添加元素的哈希代码,比如

    for (E e : this)
      if(e == this) continue; //contrived rules
      hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    
    使用这样的类而不是
    ArrayList
    ,您可以

    对于
    ArrayList
    ,第二点是错误的。因此,如果面试官的意思是“有可能以这种方式找到散列码(使用数组列表)吗?”,那么答案是否定的,因为这太荒谬了。

    您定义了一个包含自身的(病理)列表

    为什么会出现堆栈溢出错误

    根据(即规范),
    列表
    的hashcode定义为其每个元素的hashcode的函数。它说:

    “列表的哈希代码定义为以下计算的结果:”

    因此,要计算
    a
    的hashcode,首先要计算
    a
    的hashcode。这是无限递归的,并很快导致堆栈溢出

    有可能用这种方式找到散列码吗


    如果你用数学术语考虑上面的算法规范,包含代码本身的<代码>列表<代码>的哈希代码是一个不可计算的函数。不可能以这种方式(使用上述算法)或任何其他方式进行计算。

    符合
    列表的哈希代码
    实现:

    返回此列表的哈希代码值。列表的哈希代码 定义为以下计算的结果:

    int hashCode = 1;
        for (E e : list)
             hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    
     int hashCode = 1;
     for (E e : list)
         hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
    
    这确保了
    list1.equals(list2)
    意味着
    list1.hashCode()==list2.hashCode()
    对于任何两个列表,
    list1
    list2
    ,按照总合同的要求

    这并不要求实现看起来与此完全相同(请参阅以获取替代方案),但是对于仅包含自身的列表,正确的哈希代码应该是一个
    x==31+x
    必须为
    true
    ,换句话说,不可能计算一致性数字。

    否,文档有答案 《公约》明确规定:

    注意:虽然允许列表将自身包含为元素,但还是要特别小心:equals和hashCode方法在这样的列表中不再有很好的定义


    除此之外,没什么可说的了——根据Java规范,您将无法为包含自身的列表计算哈希代码;其他答案详细说明了为什么会这样,但关键是它是已知的和有意的。

    因为您将列表添加到了它本身。尝试不带add语句的.hashCode()。当您将对象放入arraylist时,您正在存储对该对象的引用。在您的情况下,您正在放入一个arraylist,它本身就是引用。类似的:好的,我知道为什么会出现stackoverflow,有人能帮我解释问题2-如何找到它吗,这是不可能的,根据
    List
    接口的定义,列表的
    hashCode
    取决于其成员。假设列表是它自己的成员,它的哈希代码取决于它的
    hashCode
    ,这取决于它的
    hashCode
    。。。以此类推,导致无限递归和您遇到的
    StackOverflowError
    。现在的问题是:为什么需要一个列表来包含它自己?我可以向您保证,您可以以更好的方式实现您试图实现的任何目标,而不需要这样的递归成员身份。因此,答案是,没有办法以这种方式找到哈希代码?是的,因为递归条件不仅如此,它是以这种方式定义的。哈希代码计算是由。没有有效的实现可以跳过自身。从规范中,您可以得出,如果您找到一个
    int
    数字,其中
    x==31+x
    true
    ,那么您可以实现一条有效的捷径……我不太明白@Holger在说什么。但解决方案有两个问题:第一:这解决了问题