【问题标题】:String HashSet produces an output that doesn't match String Hash functionString HashSet 产生与 String Hash 函数不匹配的输出
【发布时间】:2018-01-26 20:37:07
【问题描述】:

我正在准备 UIL 计算机竞赛,其中一个练习题涉及一组,这里是逐字逐句:

HashSet<String> set = new HashSet<String>();
set.add("000");
set.add("212");
set.add("211");
set.add("555");
set.add("343");
System.out.println(set);

现在,我知道 HashSet 是未排序的数据结构,但显示了 2 个未排序的选项和 1 个排序的答案选项:

A) [000、211、343、212、555]

B) [000、211、212、343、555]

c) [000, 211, 555, 343, 212]

我天真地选择了B)排序的答案,而正确的答案是A,但我仍然不明白为什么。我找到了每个字符串的哈希函数生成的哈希码:

“000” - 47664

“212” - 49619

“211”-49618

“555” - 52629

“343” - 50674

据我所知,哈希集使用哈希表作为其后端。在那种情况下,根据哈希码,我不明白为什么答案不正确。我将代码插入到 java 中,它会在正确的答案选择中产生结果。这是怎么回事,HashSet 究竟是如何向自身添加项目的?

【问题讨论】:

  • 奇怪的问题。我认为不同的 JVM 会根据 hashset 的实现给出不同的结果......所以他们一定希望你理解一些较低级别的东西。

标签: java hashset


【解决方案1】:

因为后备数组可以小于哈希码,所以顺序不是仅由hashcode() 决定的,而是由hashcode() % arr.length; 决定的。后备数组的默认大小是16,所以如果你计算你列出的哈希码的模,你会得到正确的顺序。

对于具有相同哈希码的元素,最先插入的元素也是第一个被打印的元素。这是由于存储桶包含同一存储桶中所有对象的列表(并使用equals() 来确定它们是否是相同 对象,或者它们是否恰好获得了相同的存储桶)。

【讨论】:

    【解决方案2】:

    HashSet 使用 HashMap 作为其支持数据:

    此类实现Set 接口,由哈希表(实际上是HashMap 实例)支持。

    HashSet 使用HashMap 快速确定元素是否已经存在。

    HashMap 的默认容量为16

    使用默认初始容量 (16) 和默认加载因子 (0.75) 构造一个空的 HashMap

    您已添加 5 个项目,几乎不足以满足负载系数乘以容量,因此未调整大小。

    您需要知道HashMap 如何使用哈希码来确定要使用哪个存储桶。根据this answer,Java 使用hash &amp; (SIZE -1) 只提取最低n 位,其中容量为2n

    所以,桶数如下:

    “000” - 47664 -> 0

    “212” - 49619 -> 3

    “211” - 49618 -> 2

    “555” - 52629 -> 5

    “343” - 50674 -> 2

    使用Iterator 打印出集合,该Iterator 按顺序遍历存储桶。在同一个桶内,使用插入顺序。

    因此顺序为:

    1. “000” - 47664 -> 0
    2. “211” - 49618 -> 2
    3. “343” - 50674 -> 2
    4. “212” - 49619 -> 3
    5. “555” - 52629 -> 5

    这与选项“A”相匹配。

    【讨论】:

      【解决方案3】:

      “000” - 47664

      “212” - 49619

      “211” - 49618

      “555” - 52629

      “343” - 50674

      找出他们每个人的mod除以16。然后排序。如果存在桶匹配,则添加的最后一个是列表中的第一个(因为它在发生冲突时“实现为堆栈”。)

      【讨论】:

        【解决方案4】:

        1) 问题是错误的。根据Class HashSet的规范,

        它不保证集合的迭代顺序;特别是,它不保证订单会随着时间的推移保持不变。

        由于结果取决于实施,因此不应该提出问题,或者答案应该是“以上都不是”。

        2) 实际实现发生了变化。使用的哈希不是我们熟悉的key 的hashCode,而是(h = key.hashCode()) ^ (h &gt;&gt;&gt; 16)。然后将此哈希用于hash &amp; (SIZE -1)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2018-05-07
          • 1970-01-01
          • 2018-10-31
          • 1970-01-01
          • 1970-01-01
          • 2021-12-29
          • 1970-01-01
          相关资源
          最近更新 更多