[英]Why does Java *sort* HashMap entries even though it shouldn't?
A HashMap
in Java shouldn't be sorted, and doesn't guarantee order. HashMap
中的 HashMap 不应排序,也不保证顺序。 Its order can be changed throughout the lifecycle of the application and if you want a sorted map, you should use a LinkedHashMap
, or better yet, a TreeMap
.它的顺序可以在应用程序的整个生命周期中更改,如果您想要排序的 map,您应该使用
LinkedHashMap
,或者更好的是TreeMap
。
I know this , have experienced this, and this is confirmed by the official documentation .我知道,经历过, 官方文档也证实了这一点。
However, I've just wrote some code that just won't keep the HashMap
unsorted .但是,我刚刚编写了一些代码,不会保留
HashMap
unsorted 。 At first, I thought it was fluke coincidence, but I ran the code many times and the same output is shown.起初,我以为这是侥幸巧合,但我多次运行代码并显示相同的output。
Map<String, Double> map = new HashMap<>();
map.put("A", 99.5);
map.put("B", 67.4);
map.put("C", 67.4);
map.put("D", 67.3);
System.out.println("Unsorted map: " + map);
This results in:这导致:
Unsorted map: {A=99.5, B=67.4, C=67.4, D=67.3}
I assumed that the String
keys got sorted lexicographically, somehow, since they followed A
, B
, C
and D
, though, here's another example where the sorted String
keys aren't necessarily sorted by lexicographical value:我假设
String
键以某种方式按字典顺序排序,因为它们遵循A
, B
, C
和D
,但是,这是另一个示例,其中排序的String
键不一定按字典值排序:
Map<String, Integer> unsortedMap = new HashMap();
unsortedMap.put("David", 21);
unsortedMap.put("Scott", 34);
unsortedMap.put("Marcus", 31);
unsortedMap.put("Vladimir", 24);
unsortedMap.entrySet().forEach(System.out::println);
And this one results in:这导致:
Marcus=31
David=21
Vladimir=24
Scott=34
Marcus
is lexicographically greater than David
, but David
is lexicographically less than Vladimir
. Marcus
在字典上大于David
,但David
在字典上小于Vladimir
。
I assume that this part of the source code specifically is responsible for this:我假设这部分源代码专门为此负责:
static int tieBreakOrder(Object a, Object b) {
int d;
if (a == null || b == null ||
(d = a.getClass().getName().
compareTo(b.getClass().getName())) == 0)
d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
-1 : 1);
return d;
}
Note: I'm running Java 15, though, rolling back to older versions, such as Java 8 didn't change the behavior at all.注意:我正在运行 Java 15,但回滚到旧版本,例如 Java 8 根本没有改变行为。
You're right with assuming String
keys got sorted - but it is not lexicographically sorted but based on hash of the key.假设
String
键已排序是正确的 - 但它不是按字典顺序排序的,而是基于键的 hash 。
Method java.util.HashMap#put
uses method java.util.HashMap#hash
for calculating hash of key - String
in our case.方法
java.util.HashMap#put
使用方法java.util.HashMap#hash
来计算String
的键 - 在我们的例子中。
Results of java.util.HashMap#hash
for letter A - 65, letter B - 66, letter C - 67 and so on up for single-letter objects (checked for A-Za-z, can't say anything about other values). java.util.HashMap#hash
for letter A - 65, letter B - 66, letter C - 67 等单字母对象的结果(检查了 A-Za-z,不能说其他值)。 Result of java.util.HashMap#hash
for David - 65805752. java.util.HashMap#hash
for David - 65805752 的结果。
Calculated hash is then used to decide the index of bucket in underlying array java.util.HashMap#table
in the method java.util.HashMap#putVal
.计算出的 hash 然后用于在方法
java.util.HashMap#putVal
中确定底层数组java.util.HashMap#table
中存储桶的索引。 Exact part of code:代码的确切部分:
p = tab[i = (n - 1) & hash]
Which effectively takes proper index of array based on hash value.根据 hash 值有效地采用适当的数组索引。
So, yeah - order of entries not guaranteed - but it can be manipulated by knowing the exact size of underlying array of nodes and knowing hashcode of keys that are to be put into map所以,是的 - 条目的顺序不能保证 - 但它可以通过知道底层节点数组的确切大小和知道要放入 map 的键的哈希码来操纵
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.