我们能找到包含自身作为element
的list
的hashcode
吗?
我知道这是一种不好的做法,但这是面试官问的。
当我运行以下代码时,它会抛出一个StackOverflowError
:
public class Main {
public static void main(String args[]) {
ArrayList<ArrayList> a = new ArrayList();
a.add(a);
a.hashCode();
}
}
现在我有两个问题:
- 为什么会有
StackOverflowError
? - 是否可以以这种方式找到哈希代码?
接口中指定了用于符合List
实现的哈希代码:
返回此列表的哈希代码值。 列表的哈希代码 定义为以下计算的结果:
int hashCode = 1; for (E e : list) hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
这确保了
list1.equals(list2)
意味着list1.hashCode()==list2.hashCode()
任何两个列表,list1
和list2
,如Object.hashCode()
总合同的要求。
这并不要求实现看起来完全像这样(请参阅如何以与 List.hashCode(( 相同的方式计算流的哈希代码以获取替代方案(,但仅包含自身的列表的正确哈希代码将是必须true
x == 31 + x
的数字,换句话说,不可能计算出符合要求的数字。
查看AbstractList
类中hashCode
方法的骨架实现。
public int hashCode() {
int hashCode = 1;
for (E e : this)
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
return hashCode;
}
对于列表中的每个元素,这将调用hashCode
。在您的案例列表中,列表本身是唯一的元素。现在这个电话永远不会结束。该方法递归地调用自己,递归不断缠绕,直到遇到StackOverflowError
。所以你不能用这种方式找到hashCode
。
您已经定义了一个包含自身的(病理(列表。
为什么会有
StackOverflowError
?
根据javadocs(即规范(,List
的哈希码被定义为其每个元素的哈希码的函数。 它说:
"列表的哈希代码定义为以下计算的结果:">
int hashCode = 1; for (E e : list) hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
所以要计算a
的哈希码,你首先要计算a
的哈希码。 这是无限递归的,并迅速导致堆栈溢出。
可以通过这种方式找到哈希代码吗?
不。 如果你用数学术语来考虑上面的算法规范,那么包含自身的List
的哈希码是一个不可计算的函数。 不可能以这种方式(使用上述算法(或任何其他方式计算它。
不,文档有答案
列表结构的文档明确指出:
注意:虽然允许列表将自身作为元素包含,但建议格外小心:在这样的列表中不再很好地定义 equals 和 hashCode 方法。
除此之外,没有什么可说的了 - 根据Java规范,您将无法计算包含自身的列表的hashCode;其他答案详细说明了为什么会这样,但关键是它是已知的和有意的。
Ravindra的回答为第1点提供了很好的解释。对问题2发表评论:
是否可以以这种方式找到哈希代码?
这里有一些循环的东西。在此堆栈溢出错误的上下文中,这 2 个中至少有一个必须错误:
列表的哈希代码- 必须考虑其元素的哈希代码
- 列表可以成为自己的元素
现在,因为我们在处理一个ArrayList
,所以第一点是固定的。换句话说,也许你需要一个不同的实现来有意义地计算递归列表的哈希代码......可以扩展ArrayList
并跳过添加元素的哈希代码,例如
for (E e : this)
if(e == this) continue; //contrived rules
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode());
使用这样的类而不是ArrayList
,你可以。
对于ArrayList
,第二点是错误的。因此,如果面试官的意思是"是否有可能以这种方式(使用数组列表(找到哈希代码?">,那么答案是否定的,因为这很荒谬。
因为当你从同一个函数调用同一个函数时,它将创建永无止境的递归条件。为了防止此操作,JAVA 将返回java.lang.StackOverflowError
下面是解释类似场景的示例代码:
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);
}
}