具有较大值的奇怪HashSet行为

时间:2018-11-12 18:57:57

标签: java list sorting hashset

我在Java中实现了HashSet,这对我来说是一种莫名其妙的行为。我像这样实现了HashSet,并用列表的值填充了它。

HashSet<Integer> set = new HashSet<Integer>(list);

我首先使用一个列表,该列表包含从0到9的数字,以填充HashSet:

示例:{1,0,5,9,6,7,3,1,3,6,1,5,1,3,4,9,9,7}
输出:[0, 1, 3, 4, 5, 6, 7, 9]

由于HashSet通常以升序排序的方式返回值,直到现在一切正常。但是,一旦我开始使用包含较大值的列表,它便开始以一种怪异的方式返回值:

示例:{67,1,122,19,456,42,144,42,3,34,5,5,42}
输出:[1, 34, 67, 3, 5, 456, 42, 144, 19, 122]

我读到一些有关此内容取决于此处的内部哈希算法的信息:Java HashSet shows list in weird order, always starting with 3,但由于我使用完全相同的HashSet只是具有不同的值,因此更加令人困惑。

能否请我解释为什么会这样?

3 个答案:

答案 0 :(得分:6)

HashSet显然没有提供可预测的顺序。

很容易发生,在第一种情况下,哈希码(对于Integer来说,它只是整数值)都小于存储桶数,这意味着如果所有值都小于默认的存储桶数(16),它们将同时处于顺序状态。

答案 1 :(得分:4)

  

由于HashSet通常按升序返回值

简短的答案,整数0到15,一个HashSet恰好是自然顺序。但是,由于这不是文档功能,也不是您应依赖的功能,因此将来可能会改变。


长答案:

这仅由于键的哈希方式而发生。 Integer.hashCode()实现为

public int hashCode() {
    return Integer.hashCode(value);
}

调用

public static int hashCode(int value) {
    return value;
}

例如,值0到15的哈希值仅为0到15。

HashSet依次获取哈希并对其进行搅动,以使高位保持有效。

// from HashMap
static final int hash(Object key) {
    int h;
    return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}

但是,正如您所看到的,在OpenJDK Java 11中,值0到65535保持不变。

最后,保留低位以确定它们在HashSet数组中的存储位置。

// from HashMap.putVal
i = (n - 1) & hash

n是始终为2的幂的容量。由于默认容量为16,因此0到15的值保持不变。

此索引i用于确定条目在基础数组中的什么位置。

当您遍历HashSet或HashMap时,它仅从数组的第一个索引开始,然后以索引顺序(也恰好是键的自然顺序)进行迭代。

答案 2 :(得分:3)

HashSet是无序的Collection。它不保持元素插入的顺序。因此它不会总是以升序排序的值