HashMap应该是未分类的,但仍然按键排序

根据这些:

  1. http://docs.oracle.com/javase/6/docs/api/java/util/HashMap.html
  2. HashMap,LinkedHashMap和TreeMap之间的区别
  3. java beginner:密钥如何在哈希映射中排序?

JavaHashMap应该是未排序的,但它是按照Key进行排序的。

我认为这是一个问题,因为我需要插入订单数据。 所以,我使用的是LinkedHashMap 。 但我仍然困惑为什么HashMap对它进行了排序。

有谁能解释一下?

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

 public static void main(String[] args) { HashMap newHashMap = new HashMap(); newHashMap.put(2, "First"); newHashMap.put(0, "Second"); newHashMap.put(3, "Third"); newHashMap.put(1, "Fourth"); Iterator<Entry> iterator = newHashMap.entrySet() .iterator(); while (iterator.hasNext()) { Map.Entry 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 

编辑:

我尝试使用Random of Java插入50个随机数,我发现一些数据没有排序。 但是,它仍然设法对大多数整数进行排序。

随机结果:

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

这是巧合(不是真的,而是与散列算法有关)。

尝试添加

 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特别说

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

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

正int的哈希码通常只是int,所以如果你的所有键都低于Map维护的内部数组的长度,它们可能会显示为已排序。

尝试使用非常大的值,您会看到令人失望的订单消失了。 例如,使用

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

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

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

当一个项插入到HashMap中时,应该插入它的“Bin”是 – 通过使用对象的hashCode()作为数组索引来简化它。 例如,如果hashCode为2,它将被插入到Bin 2.当此“index”大于数组大小时,它将被放入Bin(index%arraySize) – 也就是说,如果hashCode为5,它将被插入Bin 1。

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

(注意:实际的算法和散列函数可能稍微复杂一点,但这是基本的想法)

这纯属巧合。 有时似乎是排序,但继续添加键,梦想将破灭。

我写了这个小程序:

 import java.util.Map; import java.util.HashMap; class MapTest { public static void main(String[] args){ int count = Integer.parseInt(args[0]); Map map = new HashMap(); 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实现的一个属性, Integer最初顺序添加(从0开始)似乎是有序的。

就像每个人都说的那样(并且正确)是你应该假设HashMap中的键没有排序。 现在,他们将LOOK排序在您的案例中,原因有两个:

1 – 您使用Integer作为键HashMap使用Java的Object类的hashCode()方法来查找它用于存储Entry实例的基础数组中的索引(包含HashMap中的值和键的内容) 。 只是发生了Integer的哈希码是它自己的值。

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

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

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

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

您无法对HashMap对象的排序做出假设。 他们将根据需要订购,实施定义。 您应该将它们视为无序数据结构。

实际上无法确保订单。

Hashmap使用哈希码来快速哈希数据以进行搜索。

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

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

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

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

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

hash()函数只是确保在每个位位置只有常数倍数的hashCodes具有有限的冲突数[使用Google来查看为什么会这样]。

事情恰巧在这里奏效了。 就是这样。