为什么HashMap有时会以自然顺序打印

时间:2017-11-17 18:33:46

标签: java hashmap

我正在阅读Java中的Hash Map,我发现Hash Maps是无序的,未分类的。因此,在使用System.out.println(HM)打印时,我们应该以键的任意顺序获取映射。例如,以下代码

HashMap<Integer,String> HM = new HashMap<>();
HM.put(16,"hello16");
HM.put(6, "hello6");
HM.put(1, "hello1");

打印{16=hello16, 1=hello1, 6=hello6},这是一个明显随机的键顺序。但是当我用HM.put(16,"hello16");替换HM.put(15,"hello15");时,它会按照键的自然顺序打印映射,is surprising and seems unlikely by chance

{1=hello1, 6=hello6, 15=hello15}

我问过一位朋友,他说这与HashMap的初始容量(= 16)有关,但他无法清楚地解释清楚。任何人都可以通过这个特定的例子解释输出中的这种差异。

3 个答案:

答案 0 :(得分:8)

hashCode的{​​{1}}是值本身。您的Integer有16个桶,这意味着分配值的存储区为HashMap,这是0到15之间的数字。

如果您的密钥在0到15范围内,则存储桶编号密钥。使用密钥key % 16> 15时,事情会变得混乱。

当您打印< 0时,条目将按桶顺序显示。也就是说,如果有的话,首先打印桶0中的密钥;然后在桶1中的一个键,依此类推。对于HashMap,其中所有键都在0到15之间,这与键顺序完全相同。

答案 1 :(得分:1)

  

代码打印{16=hello16, 1=hello1, 6=hello6},它是按键的随机顺序

订单可能看起来是任意的,但它是确定性的。该订单基于三个因素:

  • 键的哈希码值
  • 哈希表中的桶数和
  • 插入密钥的顺序(用于确定具有相同散列密钥的项目的顺序)

枚举哈希表条目通过哈希桶进行。由于散列桶的数量低于可能的散列码的范围,因此散列码的值间接转换为散列桶的索引。 Java implementation在哈希键的顶部应用补充哈希函数,然后使用按位&来获取实际索引:

static int hash(int h) {
    // This function ensures that hashCodes that differ only by
    // constant multiples at each bit position have a bounded
    // number of collisions (approximately 8 at default load factor).
    h ^= (h >>> 20) ^ (h >>> 12);
    return h ^ (h >>> 7) ^ (h >>> 4);
}
...
static int indexFor(int h, int length) {
    return h & (length-1);
}

您可以在indexFor代码中看到length预计会成为2的强大力量(这就是他们可以使用& (length-1)代替% length的原因表达)。在您的情况下,这很重要,因为整数的哈希码匹配相应整数的值。假设容量为16个桶,16个将转换为桶0,而15个转换为桶15(桶从零开始编号)。

这就是为什么在你的例子中,15的值从前向后移动。

答案 2 :(得分:0)

HashMap 使用键'hashCode将映射条目抛出到索引的存储区数组中。检索顺序或多或少是任意的。由于字符串长度相同,只有最后一个字符不同,一般都有一个等于X + last char的哈希码,其中有一个顺序。

实现类 TreeMap 的其他Map是 SortedMap ,并按键的顺序保存条目。

另一个Map实现是 LinkedHashMap ,其中的顺序是将条目放入地图中。