равенство элементов в Java-хэш-сет - PullRequest
0 голосов
/ 02 августа 2011

Мне нужно реализовать метод equals () для Предмета, который может быть помещен в хешсет его Создателя. Предмет может иметь поля, как показано ниже

class Item{
    private String isbn;
    private String name;
    private double price;
...
}

class Maker{
    private String name;
    private Set<Item> items;
    public Maker() {
        super();
        items = new HashSet<Item>();
    }
...
}

Если я реализую равенства, сравнивая три поля и напишу hashCode (), основанный на этих полях, я получу неправильные результаты, когда

1.add item to hashset
2.modify the price of item
3.try to find if item exists in hashset


@Override
public boolean equals(Object o){
    if(o == this){
        return true;
    }
    if (!(o instanceof Item)){
        return false;
    }
    Item a = (Item)o;
    if(hasSameName(a) && hasSameIsbn(a) && hasSamePrice(a)){
        return true;
    }
    else{
       return false;
    }
}

@Override
public int hashCode(){
    int hash = 41 + this.isbn.hashCode();
    hash = hash*41+ new Double(this.price).hashCode();
    hash = hash*41 + this.name.hashCode();
    return hash;
}

...
Set<Item> items = new HashSet<Item>();
Item item1 = new Item();
item1.setName("crystal bird");
item1.setIsbn("1111");
item1.setPrice(120.5);

items.add(item1);

System.out.println(items.contains(item1));//returns true
item1.setPrice(177.0);
System.out.println(items.contains(item1));//returns false

Как решить эту проблему? Должен ли я сделать hashCode () зависимым только от isbn и предположить, что он не изменится в течение срока службы элемента.

Любая помощь приветствуется

знак.

Ответы [ 4 ]

5 голосов
/ 02 августа 2011

Да, ISBN является типичным естественным идентификатором - используйте его только для equals(..) и hashCode()

1 голос
/ 02 августа 2011

Где-то в API есть золотое правило, которое вы могли пропустить:
HashCode нельзя изменять, пока объект находится в HashMap / Set!Set использует hashCode для более быстрого размещения и поиска объектов.Таким образом, вы должны предоставить хэш-код, который не изменится.Хэш-код не обязательно должен быть уникальным в каждом случае, но он обеспечит лучшее время доступа, если оно есть.

Решение уже было предложено другими: используйте ISBN в качестве хэш-кода.Я даю очень хорошую уникальность и не изменюсь!

1 голос
/ 02 августа 2011

Метод HashSet.add() (который по сути является методом HashMap.put ()) находит лучшее место в автономной внутренней таблице в соответствии с hashcode().

Если вы измените значение элемента Key в HashMap, оно изменит значение hashcode(), что приведет к ненормальному результату.

Вы должны рассматривать ISBN только как ключ к объекту, чтобы все было максимально просто.

@Override
public boolean equals(Object o){
    if( o == null ){
        return false;
    }
    if(o == this){
        return true;
    }
    if (!(o instanceof Item)){
        return false;
    }
    Item a = (Item)o;
    if( hasSameIsbn(a) ){
        return true;
    }
    else{
       return false;
    }
}

@Override
public int hashCode(){
    return (41 + this.isbn.hashCode());
}
0 голосов
/ 02 августа 2011

Да, так как ваша цена является частью вычисления hashCode и, в свою очередь, оценивает ваше равенство объектов, так как вы устанавливаете другую цену, она меняет хеш-код и, следовательно, вы не можете найти тот же элемент. Метод contains () использует equals (), чтобы проверить, содержит ли коллекция тот же объект или нет, и поскольку ваша цена является фактором проверки на равенство, после того, как вы изменили ее, она просто больше не равна, следовательно, contains () не может найти ее .

...