我正在阅读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)有关,但他无法清楚地解释清楚。任何人都可以通过这个特定的例子解释输出中的这种差异。
答案 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 ,其中的顺序是将条目放入地图中。