TreeSet没有添加所有元素?
我一直在研究不同Java集合类型的速度,并且遇到了一些奇怪的东西。 我将1,000,000个对象从静态数组添加到不同的集合类型并返回所需的时间。 这部分代码工作正常。
在进一步调查中,我注意到TreeSet
没有收到所有1,000,000个对象,并且每次都收到不同的金额。 下面是将对象从数组传输到TreeSet
:
public int treeSet(int num) { Date before = new Date(); for(int i=0; i<num; i++) { treeSet.add(personsArray[i]); } Date after = new Date(); return (int) (after.getTime() - before.getTime()); }
下面是调用treeSet()方法并测试其大小的代码。
System.out.println("\tTree set with 1,000,000 objects--" + t.treeSet(1000000)); System.out.println("Tree set contains " + t.treeSet.size() + " elements");
这个输出是:
Tree set with 1,000,000 objects--1192 Tree set contains 975741 elements
我希望有人可以向我解释为什么TreeSet
没有收到所有对象以及它为什么收到不一致的数量。
您几乎肯定会生成重复的Person对象。
在你的评论中 ,你说每个人都是从包含“数百个”名字和年龄的文本文件中的性别,名字和姓氏中随机生成的。 假设有两种性别可能性,每种名字和姓氏有300种可能性,还有100种可能的年龄值。 这可能是18,000,000个独特的人。
让我们进一步假设equals()
在这个对象上正确实现,也就是说,它正确地检查所有这些字段。
您在18,000,000种可能性的空间中使用随机特征生成1,000,000个独特的人。
直觉上,你可能会认为重复的可能性“微不足道”,但重复的可能性实际上是1.0减去epsilon。 这被称为生日问题或有时生日悖论。
如该页面所示,任何两个选择之间发生碰撞的概率都是关于的
1 – ((d-1)/ d)^ n(n-1)/ 2
其中d是域中值的数量, n是所做选择的数量。 我不完全确定,但是d = 18,000,000和n = 1,000,000的值我认为这是有用的 1.0 – 1E-323 。 ( 编辑:正确的值大约是1.0 - 2.84E-12294
。这很接近于一个。)
这种选择中预期的碰撞次数由以下公式给出:
n – d + d *((d-1)/ d)^ n
如果d = 18,000,000且n = 1,000,000,那么这可以达到约27,000。 也就是说,平均而言你会遇到27,000次碰撞。 这非常接近TreeSet中“缺失”元素的数量,这就是碰撞会表现出来的方式。 我承认我选择的数字非常接近您所看到的数字,但我的假设和结果完全合情合理。
您需要重新考虑生成存储在集合中的数据的方式。
高度自信我可以说你正在向TreeSet
添加重复项。 如果你不相信我,只需在你的treeSet
添加数字,确保数字从1
到1000000
那么你会发现你会得到你期望的结果。
一旦你清除了疑虑,那么让我们尝试对你的People类进行排序。
将以下内容添加到People类:
int id; //ensure that every people object you create has different id. eg 1 to 10m; @override public boolean equals(Object o){ if(this.getClass()!=o.getClass()) return false; else return (People (o)).id==this.id; } @override public int hashCode(){ return id; }
现在开始向你的Set添加东西。 🙂
注意此代码只是创建不同People类的简单方法的示例。 使用treeSet等进行一些测试是一种很好的方法,但不建议用于实际问题
确保People
类上的compareTo()
方法已正确实现。 Comparable
javadoc声明如下:
强烈建议(尽管不是必需的)自然排序与equals一致。 这是因为没有显式比较器的有序集(和有序映射)在与自然排序与equals不一致的元素(或键)一起使用时表现得“奇怪”。 特别地,这样的有序集(或有序映射)违反了集合(或映射)的一般契约,其根据
equals
方法来定义。例如,如果添加两个键
a
和b
使得(!a.equals(b) && a.compareTo(b) == 0)
到不使用显式比较器的有序集,则第二个add
操作返回false (并且有序集的大小不会增加)因为a
和b
从排序集的角度来看是等价的。