这个HashSet如何产生排序输出?

以下代码生成输出[1,2]即使hashset未排序。

 Set set = new HashSet(); set.add(new Integer(2)); set.add(new Integer(1)); System.out.println(set); 

这是为什么?

此行为是由几个单独的原因引起的:

  • 整数哈希自己
  • 在Java中, HashMapHashSet由数组备份
  • 它们还使用较高位修改哈希值以修改较低位; 如果散列在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特定实现会产生你所看到的结果:这是因为值1Integer哈希到位于该位置之前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以获取进一步的参考。