【问题标题】:HashMap should be unsorted but still sorts according to keyHashMap 应该是未排序的,但仍然根据 key 排序
【发布时间】:2014-03-12 01:57:08
【问题描述】:

根据这些:

  1. http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html
  2. Difference between HashMap, LinkedHashMap and TreeMap
  3. java beginner : How key gets sorted in hashmaps?

Java 中的 HashMap 应该是未排序的,但它正在相对于 Key 进行排序。

我遇到了这个问题,因为我需要插入订单数据。所以,我改用LinkedHashMap。但我仍然很困惑为什么HashMap 对其进行排序。

谁能解释一下?

我做了一个简单的例子来查看排序。

public static void main(String[] args) {

        HashMap<Integer, String> newHashMap = new HashMap<Integer, String>();
        newHashMap.put(2, "First");
        newHashMap.put(0, "Second");
        newHashMap.put(3, "Third");
        newHashMap.put(1, "Fourth");

        Iterator<Entry<Integer, String>> iterator = newHashMap.entrySet()
                .iterator();
        while (iterator.hasNext()) {

            Map.Entry<Integer, String> entry = iterator.next();
            System.out.println("Key: " + entry.getKey());
            System.out.println("Value: " + entry.getValue());
            iterator.remove();
        }

    }

结果:

Key: 0
Value: Second
Key: 1
Value: Fourth
Key: 2
Value: First
Key: 3
Value: Third

编辑:

我尝试使用RandomJava 插入50 个随机数,但发现一些数据未排序。但是,它仍然能够对大多数整数进行排序。

随机结果:

...
Key: 36
Value: random
Key: 43
Value: random
Key: 47
Value: random
Key: 44
Value: random
Key: 45
Value: random
...

【问题讨论】:

  • HashMap 不保证是未排序的。对于 0 到 11 的值,由于 HashMap 的实现方式,您将按顺序获取它们。 HashMap 通过 hashCode 将条目存储到一个数组中。 Integer 的 hashCode 与 int 值相同。
  • HashSet 在底层使用 HashMap 也是如此。

标签: java sorting data-structures hashmap


【解决方案1】:

这是一个巧合(不是真的,而是与哈希算法有关)。

尝试添加

newHashMap.put(-5, "Fifth");

最后一次。

输出将是

Key: 0
Value: Second
Key: 1
Value: Fourth
Key: 2
Value: First
Key: 3
Value: Third
Key: -5
Value: Fifth

javadoc 具体说

这个类不保证地图的顺序;特别是,它不保证订单会随着时间的推移保持不变。

【讨论】:

  • 是的。但是,为什么要对正值进行排序?
  • @najus 你必须完成HashMap 的实现,看看它如何使用你的关键对象的hashCode
  • @najus 请注意,HashMap 的不同实现可能具有不同的行为,因此您不得依赖它
  • 好的。我尝试使用Random 随机放置数据。它当然不会对数据进行排序。在问题中编辑。
【解决方案2】:

你不应该推断太多!仅仅因为三四个数字出现排序,并不意味着它们已经排序。

一个正整数的哈希码通常就是那个整数,所以如果你所有的键都小于 Map 维护的内部数组的长度,它们可能会出现排序。

尝试使用非常大的值,您会发现出现的顺序消失了。例如,使用

100,200,300,100001, 100002, 10003, 999123456, 888777666, ....

【讨论】:

    【解决方案3】:

    你不能假设它会被排序。在这个简单的示例中,它出现排序的原因是: HashMap 是从“Bins”内部构造的。这些 Bins 包含实际元素。它们基本上是驻留在数组中的小列表。

    [0] -> [ Bin0: ... ]
    [1] -> [ Bin1: ... ]
    [2] -> [ Bin2: ... ]
    [3] -> [ Bin3: ... ] 
    

    当一个项目被插入到 HashMap 中时,它应该被插入的“Bin”——为了简化一点——通过使用对象的hashCode() 作为数组索引来找到。比如如果hashCode为2,则插入Bin 2。当这个“索引”大于数组大小时,将放入Bin(index%arraySize)——即如果hashCode为5,它将被插入到 Bin 1。

    由于 HashMap 最初的内部数组大小为 10,因此在 0 和 9 之间插入 Integer 对象会巧合地将元素以正确的顺序放入数组中。 (当然,整数的 hashCode 只是它的值)。

    (注意:实际的算法和哈希函数可能会稍微复杂一些,但这是基本思想)

    【讨论】:

      【解决方案4】:

      纯属巧合。有时它似乎已排序,但继续添加键,梦想就会破灭。

      我写了这个小程序:

      import java.util.Map;
      import java.util.HashMap;
      
      class MapTest {
      
          public static void main(String[] args){
              int count = Integer.parseInt(args[0]);
              Map<Integer, Integer> map = new HashMap<Integer, Integer>();
              for (int i = 0; i < count; i++) map.put(i, i);
              System.out.println(map);
          }
      
      }
      

      运行java MapTest 20 时,我得到以下输出(为了便于阅读,换行了):

      {0=0, 1=1, 2=2, 3=3, 4=4, 5=5, 6=6, 7=7, 8=8, 9=9, 10=10, 11=11, 12=12, 13=13, 
      14=14, 15=15, 17=17, 16=16, 19=19, 18=18}
      

      这只是HashMap 的实现的一个属性,Integers 最初按顺序添加(并从 0 开始)似乎是有序的。

      【讨论】:

        【解决方案5】:

        就像每个人都在说(AND 是对的),您应该假设 HashMap 中的键没有排序。 现在他们LOOK 对您的情况进行了排序,原因有两个:

        1 - 您使用整数作为键HashMap 使用 Java 的 Object 类的 hashCode() 方法在用于存储 @ 的底层数组中查找索引987654324@ 实例(在HashMap 中包含您的值和键)。碰巧Integer 的哈希码是它自己的值。

        2 - 您没有设置HashMap 的初始大小,因此使用的是其默认初始大小(即 16)。因此,在您添加低于 0 或高于 16(包括)的密钥之前,您将看到按顺序存储的密钥。由于HashMap 通过这样做来获取索引

        int index = newKey.hashCode() % this.capacity;
        

        稍后HashMap 可能会增加其底层数组的容量,如果您插入很多键值对(如果您从事算法和数据结构研究,它决定如何以及何时这样做非常有趣),所以您最终可能会导致您的 Integer 键看起来再次排序,但实际上并不是有意排序的。

        顺便说一句,如果您的键将是整数,并且您可以估计您将拥有的最大键值,我建议直接使用数组。访问速度更快,使用的内存将相同或略少。

        【讨论】:

          【解决方案6】:

          您不能对 HashMap 对象的顺序做出假设。他们会随心所欲地订购,实现定义。您应该将它们视为无序的数据结构。

          【讨论】:

            【解决方案7】:

            实际上不能保证顺序。

            Hashmap 使用 hashcode 对数据进行哈希处理以实现快速搜索。

            你的钥匙很简单,所以它排序了。

            【讨论】:

              【解决方案8】:

              我的猜测是有根据的,但原因很可能是默认 hashCode 方法使用内存位置这一事实。小Integers 的内存位置(并且您的密钥被自动装箱到Integer)很可能是固定的:让Integer.valueOf(1) 在多次调用时返回不同的内存位置是没有意义的。最后,这些固定内存位置很可能是按升序排列的。这可以解释这种巧合,但是,需要深入研究 Integer 和 HashMap 的实现来证明这一点。

              更正:在 Integer 的情况下,“此对象的哈希码值,等于此 Integer 对象表示的原始 int 值。” (JavaDoc)。虽然数字不同,但它证实了这个想法。

              【讨论】:

              • Object.hashCode() 与内存位置无关。当您打印 Object.toString() 时,它看起来可能是一个内存位置,但事实并非如此。
              • 除非重载 hashCode 它确实返回一个内存位置 AFAIK
              • 在此处查看我的答案stackoverflow.com/a/20843303/57695 这表明hashCode 是存储在标头中的生成数字。 Javadoc 提到了一些关于“地址”的内容,但这是误导性的。
              • 不,JavaDocs 对于 Integer 是显式的:docs.oracle.com/javase/7/docs/api/java/lang/…
              • 你的意思是什么?这个 JavaDoc 没有提到内存位置。
              【解决方案9】:

              由于没有真正利用查看 Java 源代码的答案,我会这样做! :)

              调用put()函数时,内部散列函数使用对象的hashCode,生成散列索引。 [put() source]

              hash() 函数只是确保在每个位位置仅相差常数倍数的 hashCode 有一定数量的冲突 [使用 Google 了解为什么会这样]。

              巧合的是,这里的事情发生了。就是这样。

              【讨论】:

                【解决方案10】:

                提问者说:
                “Java 中的 HashMap 应该是未排序的,但它是根据 Key 排序的。”
                是,对的。我将向您展示以下示例

                Map<String, String> map1 = new HashMap<String, String>();
                map1.put("c","xxxx");
                map1.put("b","yyyy");
                map1.put("a","zzzz");
                for (String key :map1.keySet())
                  System.out.println(map1.get(key));
                
                System.out.println();
                
                Map<Integer,String> map2 = new HashMap<Integer,String>();
                map2.put(3,"xxxx");
                map2.put(2,"yyyy");
                map2.put(1,"zzzz");
                for (int key :map2.keySet())
                  System.out.println(map2.get(key));
                

                输出显示 HashMap 使用 Key 对数据进行排序

                xxxx
                yyyy
                zzzz
                
                zzzz
                yyyy
                xxxx
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-03-05
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-07-10
                  • 2019-11-22
                  • 2017-07-01
                  • 2021-11-23
                  相关资源
                  最近更新 更多