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 Jacob Hobbs    schedule 12.03.2015    source источник
comment
Каковы шансы, что элементы, которые вы добавляете в TreeSet, равны? TreeSet не допускает дубликатов.   -  person npinti    schedule 12.03.2015
comment
Вероятность того, что эти объекты будут точно такими же, ничтожно мала. Объектами являются люди со случайно сгенерированным полом, именем, фамилией и возрастом. Имена взяты из файла .txt, содержащего сотни разных имен.   -  person Jacob Hobbs    schedule 12.03.2015
comment
Просто для подтверждения вы можете изменить treeSet.add(personsArray[i]); на if(!treeSet.add(personsArray[i])) { System.out.println("Collision Found"); }.   -  person npinti    schedule 12.03.2015
comment
Большое спасибо за вашу помощь. Оказывается, были добавлены дубликаты (я не полностью понял метод compareTo() и его значение).   -  person Jacob Hobbs    schedule 13.03.2015
comment
Очень маловероятно, что TreeSet содержит ошибку. Так что, вероятно, есть ошибка в вашей программе или рассуждениях. Так что вам действительно следует опубликовать код класса Person.   -  person Raedwald    schedule 16.03.2015
comment
См. также stackoverflow.com/questions/22123336/   -  person Raedwald    schedule 16.03.2015


Ответы (3)


Вы почти наверняка создаете повторяющиеся объекты Person.

В своем комментарии вы сказали, что каждый человек генерируется случайным образом из пола, имени и фамилии из текстового файла, содержащего «сотни» имен и возраст. Допустим, есть два варианта пола, по 300 вариантов имени и фамилии и 100 возможных значений возраста. Это в общей сложности 18 000 000 возможных уникальных людей.

Далее предположим, что equals() правильно реализован на этом объекте, то есть правильно проверяет все эти поля.

Вы создаете 1 000 000 уникальных людей, используя случайные характеристики из 18 000 000 возможностей.

Интуитивно вы можете подумать, что существует «мизерная» вероятность дубликатов, но на самом деле вероятность существования дубликатов составляет около 1,0 минус эпсилон. Это известно как проблема дня рождения или иногда парадокс дня рождения.

Как указано на этой странице, вероятность столкновения между любыми двумя вариантами примерно равна

1 - ((d-1) / d) ^ n(n-1)/2

где d — количество значений в домене, а n — количество сделанных выборов. Я не совсем уверен, но со значениями d = 18 000 000 и n = 1 000 000, я думаю, получится примерно 1.0 - 1E-323. (EDIT: правильное значение — около 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, именно так будут проявляться коллизии. Я признаю, что подбирал свои цифры так, чтобы они были довольно близки к тому, что вы видите, но мои предположения и результаты вполне правдоподобны.

Вам нужно переосмыслить способ генерации данных, которые вы сохраняете в наборе.

person Stuart Marks    schedule 13.03.2015
comment
Если кому-то интересно, как я рассчитал вероятности: stuartmarks .wordpress.com/2015/03/17/math-it-works-bitches - person Stuart Marks; 19.03.2015

с высокой степенью уверенности могу сказать, что вы добавляете дубликаты в свой TreeSet. если вы мне не верите, просто добавьте числа к treeSet, убедитесь, что числа от 1 до 1000000, тогда вы увидите, что получите именно то, что ожидаете.

Как только вы рассеете свои сомнения, давайте попробуем отсортировать ваш класс 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;
}

теперь начните добавлять вещи в свой набор. :)

ПРИМЕЧАНИЕ Этот код является просто примером простого подхода к созданию различных классов людей. Это хороший подход к тестированию с помощью treeSet и т. Д., Но это не рекомендуется для реальных проблем.

person nafas    schedule 12.03.2015

Убедитесь, что метод compareTo() в вашем классе People реализован правильно. В Comparable javadoc указано следующее:

Настоятельно рекомендуется (хотя и не требуется), чтобы естественные упорядочения соответствовали равным. Это так, потому что отсортированные наборы (и отсортированные карты) без явных компараторов ведут себя «странно», когда они используются с элементами (или ключами), чей естественный порядок несовместим с равными. В частности, такой отсортированный набор (или отсортированная карта) нарушает общий контракт для набора (или карты), который определен в терминах метода equals.

Например, если добавить два ключа a и b таким образом, что (!a.equals(b) && a.compareTo(b) == 0) к отсортированному набору, который не использует явный компаратор, вторая операция add вернет false (и размер отсортированного набора не увеличится), поскольку a и b эквивалентны с точки зрения отсортированного набора.

person Jack    schedule 12.03.2015