TreeSet没有添加所有元素?

时间:2015-03-12 11:52:57

标签: java collections tree set 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没有收到所有对象以及为什么收到不一致的金额。

3 个答案:

答案 0 :(得分:33)

您几乎肯定会生成重复的Person对象。

在你的comment中,你说每个人都是从包含“数百个”名字和年龄的文本文件中的性别,名字和姓氏中随机生成的。假设性爱有两种可能性,每个名字和姓氏有300种可能性,还有100种可能的年龄值。这可能是18,000,000个独特的人。

让我们进一步假设equals()在此对象上正确实现,即它正确检查所有这些字段。

您在18,000,000种可能性的空间中使用随机特征生成1,000,000个独特的人。

直观地说,您可能认为重复的可能性“微不足道”,但重复的可能性实际上约为1.0减去epsilon。这被称为Birthday Problem或有时候是生日悖论。

如该页面所示,任何两个选择之间发生碰撞的概率约为

  

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中“缺失”元素的数量,这就是碰撞会表现出来的方式。我承认我选择的数字非常接近您所看到的数字,但我的假设和结果完全合情合理。

您需要重新考虑生成存储在集合中的数据的方式。

答案 1 :(得分:1)

高度自信我可以说你正在为你的TreeSet添加重复项。如果你不相信我,只需在你的treeSet添加数字,确保数字从11000000,然后你会看到你会得到你期望的数字。< / p>

一旦你清除了疑虑,那就让我们尝试对你的People类进行排序。

将以下内容添加到您的人员类:

int id;    //ensure that every people object you create has different id. e.g. 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 Class的简单方法的示例。使用treeSet等进行一些测试是一种很好的方法,但不建议用于实际问题

答案 2 :(得分:0)

确保compareTo()类的People方法正确实施。 Comparable javadoc声明如下:

  

强烈建议(尽管不是必需的)自然排序   与平等一致。这是因为排序集(和排序映射)   没有明确的比较器在使用时表现得“奇怪”   元素(或键)的自然顺序与equals不一致。在   特别是,这样的有序集(或有序地图)违反了一般合同   对于集合(或映射),它是根据equals定义的   方法

     

例如,如果添加两个键ab,那么   (!a.equals(b) && a.compareTo(b) == 0)到排序   设置不使用显式比较器,第二个add   operation返回false(并且有序集的大小不会增加)   因为ab与排序集相同   透视图。