Java HashSet中元素的排序

12
为什么第二个和第三个集合会保留顺序:
Integer[] j = new Integer[]{3,4,5,6,7,8,9};
LinkedHashSet<Integer> i = new LinkedHashSet<Integer>();
Collections.addAll(i,j);
System.out.println(i); 

HashSet<Integer> hi = new HashSet<Integer>(i);
System.out.println(hi); 

LinkedHashSet<Integer> o = new LinkedHashSet<Integer>(hi);
System.out.println(o); 

这是我的输出结果:

3,4,5,6,7,8,9
3,4,5,6,7,8,9
3,4,5,6,7,8,9

https://dev59.com/Z3E85IYBdhLWcg3wl0nF#2704640 - tenorsax
通常情况下,使用哈希实现的映射(或集合)没有明确定义的顺序。(尽管某些实现,例如LinkedHashSet,具有按添加顺序排序的功能;如果有任何保证,请参阅[类级别]文档。该行为在HashSet和LinkedHashSet文档中都有讨论。) - user166390
1
Behrang说这只是巧合,他是正确的。主要原因是整数哈希码是有序的。尝试向哈希集添加多个数字以及更大的数字,看看元素的顺序是否保持不变。 - Brent Worden
2个回答

21

第二个(只使用HashSet)只是巧合。来自于JavaDocs的说明:

该类实现了Set接口,由哈希表(实际上是HashMap实例)支持。它不保证集合的迭代顺序;特别地,它不保证顺序会随时间保持不变。该类允许空元素。

第三个(LinkedHashSet)是设计成这样的:

实现Set接口的哈希表和链表,具有可预测的迭代顺序。这个实现与HashSet不同之处在于它维护一个双向链表,通过其所有条目。这个链接列表定义了迭代顺序,即元素插入集合的顺序(插入顺序)。请注意,如果将元素重新插入到集合中,插入顺序不会受到影响。(如果在调用s.contains(e)之前立即返回true,则在集合s中重新插入元素e时调用s.add(e))。

6
@Behrang的回答很好,但更具体地说,HashSet看起来与LinkedHashSet相同的唯一原因是integer.hashCode()碰巧是整数值本身,所以数字恰好按顺序存储在HashSet内部。这是高度实现特定的,正如@Behrang所说,真的只是巧合。
例如,如果您使用new HashSet<>(4),将初始桶数设置为4(而不是16),则可能会得到以下输出:
HashSet<Integer> hi = new HashSet<Integer>(4);
...
[3, 4, 5, 6, 7, 8, 9]
[8, 9, 3, 4, 5, 6, 7]
[8, 9, 3, 4, 5, 6, 7]

如果您卡在值大于或等于16的情况下,您可能会得到以下结果:
Integer[] j = new Integer[] { 3, 4, 5, 6, 7, 8, 9, 16 };
...
[3, 4, 5, 6, 7, 8, 9, 16]
[16, 3, 4, 5, 6, 7, 8, 9]
[16, 3, 4, 5, 6, 7, 8, 9]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接