我实现了一个HashMap并给出了这个输出(Output1) 你能否解释哪个元素将存储在哪个桶中。
import java.util.*;
import java.lang.*;
import java.io.*;
class Dog
{
public int i;
public int hashCode()
{
return i+3; // hashcode1
}
Dog(int i)
{
this.i = i;
}
public String toString()
{
return i + "" ;
}
}
class ShellClass
{
public static void main (String[] args) throws java.lang.Exception
{
HashSet s = new HashSet(5,(float)0.8);
for(int i=1; i<=4; i++)
{
s.add((new Dog(i)));
}
System.out.println(s);
}
}
输出是:
Output1 : [1, 2, 3, 4] //with hashcode1
但是,如果将hashcode更改为以下内容:
public int hashCode()
{
return i%3; //hashCode2
}
输出更改为:
Output2: [3, 1, 4, 2] //with hashcode2
答案 0 :(得分:1)
HashSet documentation不保证迭代器返回元素的方式有任何特定顺序。推断其toString方法不保证任何特定顺序似乎是合理的。
因此,要预测哪个元素将存储在哪个存储桶中需要了解您正在使用的特定HashSet实现的源代码(这取决于您的Java运行时链接到哪个标准库实现,但它可能是Oracle& #39; S)
(除了特定哈希集的当前容量和加载因子之外,我们还需要知道源代码,但是你在构造函数调用中提供了这些信息,所以我想我们可以假设这是给定的。)
无论如何,你可以看到source code here。 (它实际上是HashMap
的源代码,但这就是HashSet
在引擎盖下使用的内容。)
它的工作方式,它通过表达式h
从哈希码l
和(可能是正的)表长h & (l-1)
计算一个桶索引。为什么需要这样做?那么,任意对象的哈希码h
不一定在表的长度范围内;这个&
- 表达式将确保结果索引在表长度的范围内。
因此,当您在自己的类中修改hashCode计算时,它会更改通过h & (l-1)
生成的计算索引。
(警告:h
的值实际上可能不是调用hashCode
的直接结果。特别是HashMap
实现有一个帮助方法hash
获取hashCode
的结果,并以确定的方式将其转换为其他数字。)
非常重要说明:有一个合同所有 Java类应该坚持:java.lang.Object
定义的接口。特别是,Object
的文档定义了rule:&#34;如果两个对象根据equals(Object)方法相等,则调用hashCode
方法两个对象中的每一个都必须产生相同的整数结果。&#34; (这实际上只是那里描述的多部分规则的一部分。)
您的代码实际上遵守此规则,因为您没有覆盖equals
方法,因此您继承了默认方法,这实现了&#34;最具辨别力的等价关系。&#34 ;但是,如果您要自己覆盖equals
,则必须确保equals
的代码与hashCode
的代码一致。
(读者练习:如果l
是3之类的数字,则表达式h & (l-1)
总是给出结果0或2,具体取决于h
的值。错过了索引1处的潜在进入,从而浪费了桌面内的空间。浪费的空间听起来像是坏事; linked implementation实际上是否遭受这个假设性问题?)