Подтвердить что ты не робот

HashSet содержит проблему с пользовательскими объектами

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

Тест My 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) пройдет. Почему он возвращает ложь? благодаря шри

4b9b3361

Ответ 1

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

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

Ответ 2

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 в вашем случае не указано.

Ответ 3

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

До:

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.

Ответ 4

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

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

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

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

Ответ 5

Вы должны переопределить hasCode и равный метод для его удаления. вы можете сделать это следующим образом

class Price{

    private String item;
    private int price;

    public Price(String itm, int pr){
        this.item = itm;
        this.price = pr;
    }

    public int hashCode(){
        System.out.println("In hashcode");
        int hashcode = 0;
        hashcode = price*20;
        hashcode += item.hashCode();
        return hashcode;
    }

    public boolean equals(Object obj){
        System.out.println("In equals");
        if (obj instanceof Price) {
            Price pp = (Price) obj;
            return (pp.item.equals(this.item) && pp.price == this.price);
        } else {
            return false;
        }
    }

    public String getItem() {
        return item;
    }
    public void setItem(String item) {
        this.item = item;
    }
    public int getPrice() {
        return price;
    }
    public void setPrice(int price) {
        this.price = price;
    }

    public String toString(){
        return "item: "+item+"  price: "+price;
    }
}