简体   繁体   English

为什么 Java *sort* HashMap 条目即使不应该这样做?

[英]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键以某种方式按字典顺序排序,因为它们遵循ABCD ,但是,这是另一个示例,其中排序的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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM