这个HashSet如何产生排序输出?
以下代码生成输出[1,2]
即使hashset未排序。
Set set = new HashSet(); set.add(new Integer(2)); set.add(new Integer(1)); System.out.println(set);
这是为什么?
此行为是由几个单独的原因引起的:
- 整数哈希自己
- 在Java中,
HashMap
和HashSet
由数组备份 - 它们还使用较高位修改哈希值以修改较低位; 如果散列在0..15范围内,则不会被修改
- 对象的内容取决于修改后的散列的低位
- 当迭代地图或集合时,内部表将按顺序扫描
因此,如果向hashmap / hashset添加几个小的(<16)整数,则会发生以下情况:
- 整数
i
有哈希码i
- 因为它小于16,它的修改哈希也是
i
- 它落在桶里没有。
i
- 在迭代时,桶被顺序访问,所以如果你存储的所有都是小整数,它们将按升序检索
请注意,如果存储桶的初始数量太小,整数可能会落在之后未编号的存储桶中:
HashSet set = new HashSet<>(4); set.add(5); set.add(3); set.add(1); for(int i : set) { System.out.print(i); }
打印153
。
根据文档的HashSet
不保证任何顺序概念,因此您所看到的内容在未来的Java更新中可能会发生很大变化。
但是,如果你想知道为什么Java(截至目前) HashSet
特定实现会产生你所看到的结果:这是因为值1
的Integer
哈希到位于该位置之前的HashMap
的内部条目表中的某个位置2
哈希值(注意HashSet
实际上是由具有任意值的HashMap
支持的)。 这是有道理的,因为Integer
对象的哈希码只是它的值。
实际上,即使添加更多数字(在一定范围内:条目表的大小默认为16),您也可以看到这一点:
Set set = new HashSet<>(); set.add(2); set.add(1); set.add(4); set.add(3); set.add(0); System.out.println(set);
[0,1,2,3,4]
通过迭代内部条目表来进行HashSet
的迭代,这意味着表中的前面的项首先出现。
HashSet是一个无序集合。 它没有任何保证,也没有“订购”的概念。 有关详细信息,请参阅此答案: Set和List之间有什么区别?
如果需要有序的排序Set,可以考虑使用TreeSet 。
对于未排序的有序Set,还有一个LinkedHashSet 。
java中的一个set
不应该是有序列表。 请改用ArrayList
。 还要检查Java Collection API以获取进一步的参考。