哪个元素将存储在哪个桶中?

时间:2017-03-13 11:26:46

标签: hash collections hashset

我实现了一个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

1 个答案:

答案 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实际上是否遭受这个假设性问题?)