HashMap中检索到的值的顺序是否按插入顺序排列?

68

我正尝试弄清楚如何检索HashMap中值的顺序。以下是相应的代码片段。

import java.util.HashMap;

public class HashMapExample {

   public static void main(String[] args) {
       HashMap<Integer, String> hashmap = new HashMap<Integer, String>();
       hashmap.put(1, "apple" );
       hashmap.put(2, "lemon" );
       hashmap.put(3, "orange" );
       hashmap.put(4, "banana" );
       hashmap.put(5, "litchi" );
       hashmap.put(6, "mango" );
       hashmap.put(7, "papaya" );

       System.out.println(hashmap.size());

       for (String key : hashmap.values()) {
           System.out.println(key);
       }
   }
}

输出:

7
apple
lemon
orange
banana
litchi
mango
papaya

这些值被以插入顺序打印出来。这通常是真的吗?我本来期望这些值以任意顺序被打印出来。这是使用Java 6。


8
请使用 LinkedHashMap - EntangledLoops
@Raedwald 好的,但她确实多次询问了插入顺序,似乎LinkedHashMap值得一看。 - EntangledLoops
7个回答

94

根据Javadoc文档:HashMap类“不保证映射表的顺序;特别是,它不保证顺序随时间的变化而保持不变。”

如果需要有序的映射表,可以使用LinkedHashMap(按插入/访问顺序),或 TreeMap(按比较顺序)。请注意,这些保持键的顺序,而不是值的顺序。


3
我认为OP想知道为什么在他的使用情况下HashMap 似乎 保留了插入顺序。很明显,他 期望 迭代顺序更加随机。 - Stephen C
1
尽管排序是针对键的,但大多数这些映射实际上存储键/值条目(Map.Entry)。因此,与其关联的值通常也会以相同的顺序出现。 - C. K. Young
1
“我们不保证顺序”和“我们保证顺序看起来和/或实际上是随机的”之间有区别。没有理由让OP期望数据看起来是随机的。 - ggorlen

83
值按照插入的顺序打印出来。这个通常是正确的吗?我原以为值会以随机顺序打印出来。
HashMap的API没有定义迭代的顺序。
然而,如果你查看HashMap的实现,你会发现迭代顺序、键的哈希值、键的插入顺序和哈希表的大小之间存在着复杂的瞬时关系。如果哈希表自动调整大小,这种关系会被打乱。
在你的情况下,你使用的是Integer类型的键,这意味着键的哈希值就是键的值本身。此外,你按照键的顺序插入了条目。这使得(幸运地!)迭代顺序与插入顺序相匹配。但是,如果你继续插入更多的键,你会发现迭代顺序会“循环”。然后,当表经历一系列的调整大小操作时,顺序会变得越来越混乱。
简而言之,你所看到的是哈希表实现和特定的hashCode方法的产物。这并不是你可以(或者应该)合理利用的东西。尤其是因为它可能会在不同的Java版本中发生改变(已经发生过改变)!
1 - 或者在Java 8或更高版本中,如果特定哈希桶/哈希链的拥塞导致它从简单列表切换到红黑树。这是一个深层次的实现细节。如果你感兴趣,可以阅读源代码!

11
一个LinkedHashMap是你想要的。从文档中可以看出,它与HashMap的不同之处在于,它维护一个双向链表穿过所有的条目。

7
如果顺序很重要,请尝试使用LinkedHashMap...请参阅JavaDoc public class LinkedHashMap扩展了HashMap类,实现了Map接口,采用哈希表和链表相结合的方式,并按照可预测的迭代顺序进行排序。这种实现方式与HashMap不同之处在于,它维护着一个双向链表,该链表遍历所有条目。该链表定义了迭代顺序,通常是键插入到映射中的顺序(插入顺序)。请注意,如果将键重新插入到映射中,则不会影响插入顺序。(当m.containsKey(k)在调用之前立即返回true时,如果调用m.put(k, v),则键k将被重新插入到映射m中。)

1
一个相关的集合是java.util.concurrent的ConcurrentSkipListMapSkiplist允许您按键顺序遍历条目,并以随机顺序查看它们(但不像HashMap那样快)。
有一个很好的跳表演示小程序

0
Stephen C的回答是正确的,解释了你所看到的细节。
此外,从Java 21开始,还有另一种观察这个问题的方式。Java 21带来了有序集合

SequencedMap

现在,具有定义的遭遇顺序的Map实现由接口SequencedMap表示,从Java 21+开始。
长期以来,根据键的内容对条目进行排序的地图一直由接口SortedMap及其后继者NavigableMap表示。但是这两个接口并未涵盖其他排序方式。具体来说,它们没有涵盖按插入顺序跟踪的LinkedHashMap。
新的SequencedMap接口涵盖了所有这些内容。Java中内置的每个地图接口和实现都有一个定义的遭遇顺序,都被SequencedMap所涵盖。
因此,我们可以通过查看SequencedMap的Javadoc来回答您的问题“HashMap是否根据插入顺序保持遭遇顺序”。该接口的Javadoc列出了三个具体的实现,而HashMap不是其中之一。所以,不,HashMap不保持遭遇顺序。

-1
不,HashMap在没有排序实现的情况下不会保留顺序。 如果您想要按键排序,可以使用TreeMap。
例如 - {[3=1],[2=50],[20=4],[14=1]} -> HashMap 对于TreeMap,您将获得 - {[2=50],[3=1],[14=1],[20=4]} -> TreeMap

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接