HashSet содержит проблему с пользовательскими объектами - PullRequest
15 голосов
/ 24 февраля 2011

Мой пользовательский класс, который будет содержать HashSet

public class Person {
    String name;
    int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "hashcode='" + this.hashCode() + '\'' +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Person)) return false;

        Person person = (Person) o;

        if (age != person.age) return false;
        if (!name.equals(person.name)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = name.hashCode();
        result = 31 * result + age;
        return result;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

Мой тест HashSet не прошел

   public void hashSetTest() {
        Set<Person>  personSet = new HashSet<Person>();
        Person p1 = new Person("raghu", 12);
        Person p2 = new Person("rimmu", 21);

        personSet.add(p1);
        personSet.add(p2);


       p1.setName("raghus");
       p1.setAge(13);

       int i2 =p1.hashCode();
       System.out.println(personSet.size() + ": "+ p1.hashCode()+" : "+personSet.contains(p1)+ " : "+i2);
    }

Я ожидаю, что personSet.contains (p1) пройдет. Почему он возвращает ложь? Спасибо шри

Ответы [ 4 ]

29 голосов
/ 24 февраля 2011

Потому что p1.hashCode() изменяется при изменении p1, поэтому его больше нельзя найти по исходному индексу в хэш-таблице.Никогда не допускайте, чтобы значение хеш-функции зависело от изменяемого поля.

(Вам очень повезло, что он потерпел неудачу во время тестирования; возможно, он также преуспел, но только в работе).

5 голосов
/ 25 февраля 2011

HashSet реализует Set .ApiDoc задает:

Note: Great care must be exercised if mutable objects are used as set elements. The behavior of a set is not specified if the value of an object is changed in a manner that affects equals comparisons while the object is an element in the set.

В вашем примере это так, потому что изменение name или age на p1 влияет на сравнение равных.Таким образом, согласно ApiDoc, поведение Set в вашем случае не определено.

1 голос
/ 07 апреля 2011

Я думаю, вам нужно, чтобы hashCode довольно часто зависел от изменяемых полей: когда вы переопределяете equals, это зависит от изменяемых полей.

Из контракта hashCode: «Если два объекта равны в соответствии с методом equals (Object), то вызов метода hashCode для каждого из двух объектов должен давать один и тот же целочисленный результат.»

Итак, если вы создаете два объекта, таких как A.equals (B), истинно, а затем модифицируете A таким образом, что A.equals (B) становится ложным, вам также нужно изменить hashCodes.

Это правда, что в документации hashCode указано, что «Не требуется, чтобы, если два объекта были неравны в соответствии с методом equals (java.lang.Object), то вызов метода hashCode для каждого из двух объектов должен привести к целочисленные результаты. ", но я не знаю, как это может помочь.

1 голос
/ 25 февраля 2011

Хэши - это простая пара ключей и значений. Вот как будет выглядеть состояние вашего кода до и после переименования в псевдокоде:

До:

personSet => {
    SOME_NUM1 => Person(name=>"raghu", 12),
    SOME_NUM2 => Person(name=>"rimmu", 21)
}

p1.setName("raghus"); #p1.hashcode() = SOME_NEW_NUM
p1.setAge(13);#p1.hashcode() = SOME_OTHER_NEW_NUM

После того, как:

personSet => {
    SOME_NUM1 => Person(name=>"raghu", 13),
    SOME_NUM2 => Person(name=>"rimmu", 21)
}

Поскольку у вас есть прямой доступ к p1, объект в HashSet обновляется правильно, но HashSet не обращает внимания на обновляемые хеш-коды содержащихся объектов. Когда вызывается personSet.contains(p1), HashSet ищет запись с новым значением p1.hashcode().

Объект p1 связан с его предыдущим хеш-кодом в тот момент, когда он был добавлен в HashSet.

...