Hashmap +插入顺序

时间:2015-01-20 14:21:08

标签: java

我读到了关于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}

3 个答案:

答案 0 :(得分:10)

  

我执行了以下代码,并且以相同的顺序返回10,000次响应。

这正是发生与您正在使用的版本以及您要插入的值一起发生的情况。无法保证它会继续发生,或者如果您添加不同的值,或者如果您删除项目然后添加其他值,则会保留插入订单。

基本上,并不是说它绝对不会按特定顺序 - 它说你绝对应该依赖于它的顺序。< / p>

另请注意,如果您希望维护插入顺序,那么您的示例已经证明它不是。输出显示按插入顺序显示的项目

LinkedHashMap将维护插入顺序,方法是在内部维护哈希映射旁边的条目链接列表。 (这里的一个例外是,有一个构造函数允许您指定项目将以访问顺序而不是插入顺序呈现,这通常在您希望将其作为缓存的基础时使用。)

答案 1 :(得分:3)

HashMap的自然实现会丢失有关其元素插入Map的顺序的信息。确实如此,这意味着如果不明确跟踪它,您将丢失此信息。这并没有说明两个相同填充的HashMap的内容和内容顺序。插入元素时,算法会将其放在Map中最近的位置(这些对您来说是隐藏的)。如果将相同顺序的相同对象放入两个映射中,那么它们只是逻辑上看起来相似。您仍然会丢失有关将元素放入地图的顺序的信息。

答案 2 :(得分:2)

从您的示例代码中可以看出,HashMap的给定String组的输出顺序将始终相同,具体取决于输入顺序,因为:

  1. HashMap使用hashCode
  2. 字符串is consistent for any given String
  3. 上的哈希码

    但是,当前表单中的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键输入序列生成不同的顺序。