我读到了关于HashMap的内容,并表示不会维护插入顺序。我执行了以下代码,并以相同的顺序返回10,000次响应。
同样在密钥中,我只是将前缀从E更改为M.有人可以帮助解释这种行为吗?
for ( int i = 0; i < 10000; i++ ) {
Map<String, String> map1 = new HashMap<String, String>();
map1.put( "E._AUTO", "20");
map1.put( "E._ITERATIVE", "20");
map1.put( "E._ANDREW", "20");
System.out.println(map1);
Map<String, String> map2 = new HashMap<String, String>();
map2.put( "M._AUTO", "20");
map2.put( "M._ITERATIVE", "20");
map2.put( "M._ANDREW", "20");
System.out.println(map2);
}
输出:
{E._ANDREW=20, E._ITERATIVE=20, E._AUTO=20}
{M._ITERATIVE=20, M._AUTO=20, M._ANDREW=20}
答案 0 :(得分:10)
我执行了以下代码,并且以相同的顺序返回10,000次响应。
这正是发生与您正在使用的版本以及您要插入的值一起发生的情况。无法保证它会继续发生,或者如果您添加不同的值,或者如果您删除项目然后添加其他值,则会保留插入订单。
基本上,并不是说它绝对不会按特定顺序 - 它说你绝对应该不依赖于它的顺序。< / p>
另请注意,如果您希望维护插入顺序,那么您的示例已经证明它不是。输出显示按插入顺序显示的项目不。
LinkedHashMap
将维护插入顺序,方法是在内部维护哈希映射旁边的条目链接列表。 (这里的一个例外是,有一个构造函数允许您指定项目将以访问顺序而不是插入顺序呈现,这通常在您希望将其作为缓存的基础时使用。)
答案 1 :(得分:3)
HashMap的自然实现会丢失有关其元素插入Map的顺序的信息。确实如此,这意味着如果不明确跟踪它,您将丢失此信息。这并没有说明两个相同填充的HashMap的内容和内容顺序。插入元素时,算法会将其放在Map中最近的位置(这些对您来说是隐藏的)。如果将相同顺序的相同对象放入两个映射中,那么它们只是逻辑上看起来相似。您仍然会丢失有关将元素放入地图的顺序的信息。
答案 2 :(得分:2)
从您的示例代码中可以看出,HashMap
的给定String
组的输出顺序将始终相同,具体取决于输入顺序,因为:
hashCode
但是,当前表单中的Java HashMap
可能会在插入更多元素(更改哈希表大小)或更改同一字符串集的不同插入顺序时更改排序。例如:
for (int i = 1; i < 15; i++) {
//for (int i = 14; i > 0; i--) {
map1.put(String.format("%04d", i), "");
System.out.println(String.format("%04d:", i) + map1);
}
向前和向后运行会在14次插入后产生不同的迭代次序。插入&#34; 0013&#34;之间和&#34; 0014&#34; (向前运行)最后几个元素是相同的,但迭代顺序改变了:
0013:{... 0012=, 0003=, 0011=, 0002=, 0010=, 0009=, 0008=} 0014:{... 0003=, 0012=, 0002=, 0011=, 0009=, 0008=, 0010=}
这可能看起来是随机的,但是再次运行它会以完全相同的方式发生。因此,当插入元素时,这个特定的实现在它的排序中是不可预测的,但是在给定相同的起始和输入条件的情况下确定性。我强调 implementation ,因为在J7(u6 +)中,您可以使用java -Djdk.map.althashing.threshold=<threshold>
为各种基于散列的集合更改此行为,以便在同一台计算机上的不同JVM实例中,此行为变为不可预测的。
LinkedHashMap
将维护迭代顺序(normally insertion-order)。如果您想要解决两者之间的差异,您可以使用非基于值的hashCode
更清楚地看到结果。您可以将String
包装为:
class StrCont {
private String s;
public StrCont (String s) { this.s = s; }
public String toString() { return this.s; }
// uses the Object.hashCode implementation
}
StrCont
课程使用hashCode
中的默认Object
。因此,(generally)是对象的内存位置的hexString;包裹的String
变得与哈希无关。使用 作为您的密钥:
map1.put( new StrCont("E._AUTO"), "20");
map1.put( new StrCont("E._ITERATIVE"), "20");
map1.put( new StrCont("E._ANDREW"), "20");
// need only 5/6 more than this to highlight the differences
多次重复此操作会产生具有相同String
&#34;值&#34;的新对象引用,但完全不同hashCode
s。订单已完全销毁HashMap
,并为LinkedHashMap
维护。
TLDR :当前JRE的hashCode
实施中基于值的String
(例如来自HashMap
的那些)是令人分心的案例其中,由于您选择的实施(也是基于内部状态的)确定性,您可能会开始认为所有HashMap
都基于hashCode
给出一致的排序。
但是如果依赖于一致的迭代排序,则需要使用有序的哈希映射,例如LinkedHashMap
。
† 虽然这是真的(in J7 only)来自J7u6但是有一个hash32函数,如果它们已被切换为使用替代散列方法{{{ 3}}。因此,即使在同一台机器上重新启动给定JVM之间,也可以为相同的String键输入序列生成不同的顺序。