Java 为什么HashMap有时会按自然顺序打印
我在介绍Java中的哈希映射时发现,哈希映射是无序和未排序的。因此,在使用Java 为什么HashMap有时会按自然顺序打印,java,hashmap,Java,Hashmap,我在介绍Java中的哈希映射时发现,哈希映射是无序和未排序的。因此,在使用System.out.println(HM)打印时,我们应该以键的任意顺序获得映射。例如,下面的代码 HashMap<Integer,String> HM = new HashMap<>(); HM.put(16,"hello16"); HM.put(6, "hello6"); HM.put(1, "hello1"); 我问了一位朋友,他说这与HashMap的初始容量(=16)有关,但他无法解释清
System.out.println(HM)
打印时,我们应该以键的任意顺序获得映射。例如,下面的代码
HashMap<Integer,String> HM = new HashMap<>();
HM.put(16,"hello16");
HM.put(6, "hello6");
HM.put(1, "hello1");
我问了一位朋友,他说这与HashMap的初始容量(=16)有关,但他无法解释清楚。任何人都可以用这个特定的例子来解释输出中的这种差异。HashMap使用键的hashCode将映射项抛出到桶的索引数组中。检索顺序或多或少是任意的。由于相同长度的字符串(仅在最后一个字符中不同)通常具有等于X+最后一个字符的哈希代码,因此它们之间存在顺序 实现类TreeMap的另一个映射是SortedMap,它按键的顺序保存条目
另一个映射实现是LinkedHashMap,其中的顺序是将条目放入映射中的顺序。整数的
hashCode
是值本身。您的HashMap
有16个bucket,这意味着分配值的bucket是键%16
,它是一个从0到15的数字
如果您的钥匙在0到15之间,则桶号就是钥匙。只有当你使用键>15
或<0
时,事情才会变得一团糟
打印HashMap
时,条目按存储桶顺序显示。也就是说,如果存在存储桶0中的密钥,则首先打印该密钥;然后将钥匙插入铲斗1,依此类推。在HashMap
中,所有键都在0到15之间,这与键顺序完全相同
该代码打印{16=hello16,1=hello1,6=hello6}
,这是键的随机顺序
顺序可能是任意的,但它是确定的。订单基于三个因素:
- 密钥的哈希代码的值
- 哈希表中的桶数,以及
- 插入键的顺序(用于确定具有相同哈希键的项的顺序)
&
获取实际索引:
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);
}
您可以从代码的索引中看到,长度
应该是二的幂(这就是为什么他们可以使用&(length-1)
而不是%length
表达式)。在您的例子中,这很重要,因为整数的哈希代码匹配相应整数的值。假设容量为16个铲斗,16个将转换为铲斗零,而15个将转换为铲斗15(铲斗从零开始编号)
这就是为什么在您的示例中,15的值从前面移到后面。发生这种情况的原因并不重要,因为您不应该依赖它。在Java 9中,由于这个原因,
的Set#和的Map#的插入顺序是随机的。@RomanPuchkovskiy OP在问题中说“…当打印使用系统.out.println(HM)…”,所以我们应该以键的随机顺序获得映射”-不。HashMap不保证随机顺序。它完全没有承诺订单是什么。今天它可能是由内部桶订购的。下一个版本,它可以像Go一样随机化。在其他人的机器上,它可能像新的Python dict实现一样按插入顺序排列。“似乎不太可能是偶然的”:你有六分之一的机会有三个值按自然顺序排列,而你花了一次机会却没有得到它。你不应该依赖于顺序或桶大小。这是一个内部实现细节,可能会随着时间的推移而改变(从一个版本到另一个版本)。@tobain您完全正确。但问题是,为什么OP的当前代码会出现这种情况。
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);
}