HashSet, хранящий одинаковые объекты

Ниже приведен код для поиска повторяющихся объектов из списка объектов. Но по какой-то причине хеш-набор хранит даже одинаковые объекты.

Я, конечно, что-то здесь упускаю, но когда я проверяю размер хэш-набора, получается 5.

import java.util.ArrayList;
import java.util.HashSet;


public class DuplicateTest {

public static void main(String args[]){
    ArrayList<Dog> dogList = new ArrayList<Dog>();
    ArrayList<Dog> duplicatesList = new ArrayList<Dog>();
    HashSet<Dog> uniqueSet = new HashSet<Dog>();

    Dog a = new Dog();
    Dog b = new Dog();
    Dog c = new Dog();
    Dog d = new Dog();
    Dog e = new Dog();

    a.setSize("a");
    b.setSize("b");
    c.setSize("c");
    d.setSize("a");
    e.setSize("a");

    dogList.add(a);
    dogList.add(b);
    dogList.add(c);
    dogList.add(d);
    dogList.add(e);

    if(a.equals(d)){
        System.out.println("two dogs are equal");
    }
    else System.out.println("dogs not eqal");

    for(Dog dog : dogList){
        uniqueSet.add(dog);
    }

    System.out.println("number of unique dogs="+ uniqueSet.size());
    /*for(Dog dog:uniqueSet){
        System.out.println("uniqueset ="+dog.getSize());
    }

    for(Dog dog : duplicatesList){
        System.out.println("duplicate dog="+dog.getSize());
    }*/

}

}

А вот и класс Dog

public class Dog implements Animal, Comparable<Dog>{

String size;

public void makeNoise(){
    System.out.println("woof woof");
}

public String getSize() {
    return size;
}

public void setSize(String size) {
    this.size = size;
}

public int compareTo(Dog d){
    return this.size.compareTo(d.size);
}

public boolean equals(Dog d){
    return this.size.equals(d.size);
}

@Override
public int hashCode() {
    // TODO Auto-generated method stub
    return super.hashCode();
}
}

person antnewbee    schedule 30.03.2013    source источник


Ответы (1)


Этот код не делает то, что вам нужно:

public boolean equals(Dog d){
    return this.size.equals(d.size);
}

Это не переопределяет Object.equals, который использует HashSet. Тебе нужно:

@Override
public boolean equals(Object d){ 
    if (!(d instanceof Dog)) {
        return false;
    }
    Dog dog = (Dog) d;
    return this.size.equals(dog.size);
}

Обратите внимание: используя аннотацию @Override, вы просите компилятор проверить, действительно ли вы переопределяете метод.

РЕДАКТИРОВАТЬ: Как уже отмечалось, вам также необходимо переопределить hashCode таким образом, который совместим с вашим методом equals. Учитывая, что вы проверяете равенство на основе размера, самым простым вариантом будет:

@Override
public int hashCode() {
    return size.hashCode();
}
person Jon Skeet    schedule 30.03.2013
comment
Кроме того, OP должен переопределять hashCode по-разному, потому что, как он это делает сейчас, две разные собаки одинакового размера имеют разные хэш-коды. Поскольку OP, похоже, использует строку из одного символа для размера собаки, можно было бы использовать return size.charAt(0); - person halex; 30.03.2013
comment
@halex: Ага, не заметил. Будет редактировать. (Но я не собираюсь использовать только charAt(0)... можно также использовать хеш-код размера...) - person Jon Skeet; 30.03.2013
comment
Ваше решение с использованием хеш-кода size лучше, я должен признать :). +1 - person halex; 30.03.2013
comment
хммм.... зачем нужно реализовывать метод hashCode(). Если мой код говорит, что два объекта равны, если это условие выполнено, то почему компилятор проверяет hashCode? Я понимаю важность hashCode в HashMaps, но почему в наборе? - person antnewbee; 30.03.2013
comment
@antnewbee: Вы используете HashSet. Подсказка находится в части Hash :) Если бы вы использовали TreeSet, он использовал бы compareTo... но вы используете HashSet, который основан на простом равенстве/хешировании. (И это не компилятор вызывает hashCode — это код внутри HashSet.) - person Jon Skeet; 30.03.2013