我使用简单的HashSet来存储数字。我只是向HashSet添加了0到99999个数字。但是在65535之后,HashSet没有排序,但观察到一些不同的模式。为什么收集没有排序,虽然我正在添加已经排序的数字。为什么在65535之后观察到这种不同的模式? 65535 是否表示此示例中的内容?
代码:
import java.util.*;
class TestClass {
public static void main(String args[] ) throws Exception {
HashSet<Integer> hsset=new HashSet<>();
for(int i=0;i<100000;i++)hsset.add(i);
for(int i:hsset){
System.out.print(i+" ");
}
}
}
65535的输出差异:
65507 65508 65509 65510 65511 65512 65513 65514 65515 65516 65517 65518 65519 65520 65521 65522 65523 65524 65525 65526 65527 65528 65529 65530 65531 65532 65533 65534 65535 65537 65536 65539 65538 65541 65540 65543 65542 65545 65544 65547 65546 65549 65548 65551
答案 0 :(得分:7)
HashSet
没有保证其元素的顺序,因此您所看到的很可能是如何完成散列以及如何根据哈希值存储元素的工件。
如果您想要一个有序集,TreeSet
可能更合适。如果您只想要一个有序的集合,那么请查看ArrayList
。 (或LinkedHashSet
,作为Eran注释,维护广告订单。)
请记住, set 在数学上只是其中的一些元素(没有重复),而其他所有元素都在它之外。在彼此之间对元素进行排序并不重要,甚至不需要。但是,由于某些顺序有时对某些算法有用,因此有一些特殊的实现将此属性添加到集合的数学理想中。
答案 1 :(得分:4)
正如我在https://stackoverflow.com/a/2144822/139985中所解释的那样,您所看到的明显排序是1)Integer.hashCode()
的实现以及2)填充HashSet
的特定方式的假象
虽然您的集合中的条目似乎是有序的(直到集合达到阈值大小),但这是实施的意外结果,而不是您可以(或应该)依赖的属性上。 (这不完全是“巧合”......因为行为不是随机的。)
HashSet
API不保证订购。如果您想要保证维持订单使用的集合:
TreeSet
表示根据Comparable或Comparator语义对条目进行排序的集合,或LinkedHashSet
表示保留插入(时间)顺序的集合。答案 2 :(得分:1)
HashSet
没有订购。迭代顺序取决于元素的hashCode()
以及它们如何映射到HashSet
的区间,但这是一个实现细节。
如果按排序顺序添加元素并希望对它们进行排序(即能够按排序顺序迭代它们),请使用LinkedHashSet
,因为它维护了插入顺序。
您的元素为Integer
,而hashCode()
的{{1}}只是Integer
的{{1}}值。
int
(由Integer
使用)获取元素(或键)的HashMap
并对其执行以下转换以计算bin索引:
HashSet
如果hashCode()
小于static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
(即小于hashCode
),则表达式2^16
的值与65536
相同。
因此,将每个第一个(key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
元素放入索引为key.hashCode()
的bin中,并且由于65535
和hashCode() modulu (the number of bins)
保持负载率<1。 1(默认为0.75),bin的数量高于元素的数量。这意味着小于65536的每个HashSet
元素HashMap
都存储在bin Integer
中。
当你遍历i
的元素时,它们会根据bin的索引返回给你(首先是bin 0的元素,然后是bin 1的元素等等)。 bin 0包含值0,bin 1包含值1等,前面的65535个元素显示为已排序。
添加i
后,“订购”会中断。