【问题标题】:Which element will be stored in which bucket?哪个元素将存储在哪个桶中?
【发布时间】:2017-03-13 11:26:46
【问题描述】:

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

但是,如果哈希码更改为以下:

public int hashCode()
{
            return i%3; //hashCode2
}

输出更改为:

Output2: [3, 1, 4, 2] //with hashcode2

【问题讨论】:

    标签: hash collections hashset


    【解决方案1】:

    HashSet documentation 不保证其迭代器返回元素的特定顺序。推断它的 toString 方法也不保证任何特定的顺序似乎是合理的。

    因此,要预测哪个元素将存储在哪个存储桶中,需要了解您正在使用的特定 HashSet 实现的源代码(这取决于您的 Java 运行时链接到哪个标准库实现,但它可能是 Oracle 的)。

    (我们需要了解源代码除了特定哈希集的当前容量和负载因子,但是您在构造函数调用中提供了该信息,所以我想我们可以假设这是给定的。)

    不管怎样,你可以看到source code here。 (它实际上是HashMap 的源代码,但这是HashSet 在底层使用的。)

    它的工作方式是通过表达式 h &amp; (l-1) 从哈希码 h 和(可能是正的)表长度 l 计算存储桶索引。为什么需要这样做?嗯,任意对象的hashcodeh不一定在表的长度范围内;这个&amp;-表达式将确保生成的索引在表长度的范围内。

    因此,当您在自己的类中修改 hashCode 计算时,它会更改通过 h &amp; (l-1) 生成的计算索引。

    (警告:h 的值实际上可能不是调用hashCode直接结果。特别是,HashMap 实现有一个辅助方法hash,它采用hashCode 的结果,并以确定性的方式将其转换为不同的数字。)


    非常 重要提示:所有 Java 类都应该遵守一个约定:java.lang.Object 定义的接口。特别是,rule 的文档为 Object 定义了一个 rule:“如果两个对象根据 equals(Object) 方法相等,那么对两个对象中的每一个调用 hashCode 方法必须产生相同整数结果。” (这实际上只是那里描述的多部分规则的一部分。)

    您的代码实际上遵守了这条规则,因为您没有重写equals 方法,因此您继承了默认的方法,它实现了“最有区别的可能等价关系”。但是,如果您要自己覆盖 equals,则必须确保您的 equals 代码与您的 hashCode 代码一致。


    (读者练习:如果l 是一个类似3 的数字,则表达式h &amp; (l-1) 总是给出结果0 或2,具体取决于h 的值。这将错过索引处的潜在条目1,因此浪费了表内的空间。浪费空间听起来是件坏事;linked implementation 真的会遇到这个假设的问题吗?)

    【讨论】:

    • 感谢您的回答 pnkfelix。在Hashtable的情况下,我假设bucket是根据(hashcode方法返回的hashcode值% Hashtable的长度)计算的,然后toString的输出是这些bucket在一个bucket中从上到下从右到左的顺序。它总是给出很好的结果。我的假设正确吗?
    • 嗨,嘘;如果我的回答解决了您的问题,那么请接受答案。这样其他人就不会认为这个问题需要替代答案。 :)
    • 关于bucket是否基于hashCode() % length计算的假设,我回答:这和h &amp; (l-1)一样吗,正如我在源代码中已经展示的那样?让我们看看,如果h=4l=3 ... 那么h%l4%3 = 1,而h &amp; (l-1)4 &amp; (3-1) = 0。所以它们在所有可能的情况下都不相同,看起来......但也许你仍然在做一些事情?我建议您查看我的“读者练习”以了解更多信息...
    • 更一般地说,作为良好编码实践的问题,我认为对toString 编码中返回的元素顺序做出假设可能不是一个好主意. (我回答的前两段是为了强烈暗示这一点,但我担心 shubh 的评论我太微妙了。)我想说,如果你以某种方式依赖特定的顺序,那么你可能想要使用不同的顺序哈希表实现实际上将保证其规范中的稳定排序。 HashMap 和 HashSet 明确不这样做。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-25
    • 2016-06-01
    • 2021-10-08
    • 2011-07-27
    • 2017-11-12
    • 2020-05-29
    相关资源
    最近更新 更多