2010-11-07 2 views
15

Обычно я не код на Java, но в последнее время у меня не было выбора. Возможно, у меня возникло серьезное непонимание того, как правильно использовать HashSet. Таким образом, возможно, что-то, что я сделал, просто неправильно. Однако я благодарен за любую помощь, которую вы могли бы предложить. Таким образом, актуальная проблема:Java Hashset.contains() производит таинственный результат

В небольшой программе, которую я писал, я создавал очень похожие объекты, которые при создании имели бы очень специфический идентификатор (string или в моей последней итерации long). Поскольку каждый объект создавал новые объекты, я хотел отфильтровать все те, что я уже создал. Поэтому я начал бросать id каждого нового объекта в свой Hash (Set) и тестирование с HashSet.contains(), если ранее был создан объект. Вот полный код:

// hashtest.java 
import java.util.HashSet; 

class L { 
    public long l; 
    public L(long l) { 
     this.l = l; 
    } 
    public int hashCode() { 
     return (int)this.l; 
    } 
    public boolean equals(L other) { 
     return (int)this.l == (int)other.l; 
    } 
} 

class hashtest { 
    public static void main(String args[]) { 
     HashSet<L> hash = new HashSet<L>(); 
     L a = new L(2); 
     L b = new L(2); 
     hash.add(a); 
     System.out.println(hash.contains(a)); 
     System.out.println(hash.contains(b)); 
     System.out.println(a.equals(b)); 
     System.out.println(a.hashCode() == b.hashCode()); 
    } 
} 

производит следующий вывод:

true 
false 
true 
true  

так, по-видимому, contains не использует функцию equals предоставленную L, или у меня есть некоторые основные непонимание концепции ...

Я тестировал его с помощью openjdk (текущая версия, включенная в ubuntu) и официальная текущая Java из Oracle на Win7

для полноты официальной документации Java-API для HashSet.contains():

public boolean contains(Object o)

Возвращает true, если этот набор содержит указанный элемент. Более формально возвращает true тогда и только тогда, когда этот набор содержит элемент e такой, что (o==null ? e==null : o.equals(e)).

http://download.oracle.com/javase/6/docs/api/java/util/HashSet.html#contains(java.lang.Object)

Любые идеи или предложения?

ответ

28

Ваш equals метод должен принять Object.
Поскольку вы объявили его как принимающий L, он становится дополнительной перегрузкой вместо переопределения метода.
Поэтому, когда класс hashSet вызывает equals, он разрешает базовый метод Object.equals. Когда вы звоните equals, вы вызываете свою перегрузку, потому что a и b оба являются объявленными как L вместо Object.

Чтобы предотвратить эту проблему в будущем, вы должны добавить @Override всякий раз, когда вы переопределяете метод.
Таким образом, компилятор предупредит вас, если это не переопределение.

+1

Чтобы уточнить - если у вас нет подписи вашей версии «равно», вы фактически не перегружаете равные, а HashSet (и все остальное) будет использовать исходный метод equals, если он напрямую не знает о классе L (как и ваш тестовый код). Чтобы проверить это, добавьте: System.out.println (a.equals ((Object) b)); к вашей тестовой программе - он должен вернуть значение false. –

3

Вы фактически не переопределяете Object.equals; вместо этого вы определяете новый метод с тем же именем, но с разными параметрами. Обратите внимание, что Object.equals принимает аргумент Object, в то время как ваш метод equals принимает аргумент L.Если вы перепишете свой метод equals, чтобы принять Object и выполнить необходимую проверку/литье типов до L во время выполнения, то ваш код работает так, как вы ожидаете.

Кроме того, именно поэтому вы действительно должны использовать аннотации @Override всякий раз, когда ваш JRE поддерживает их. Таким образом, компилятор будет жаловаться, если вы случайно реализуете новый метод, когда собираетесь направить существующий.

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

@Override 
public boolean equals(Object other) { 
    return other != null && other instanceof L && this.l == ((L)other).l; 
} 
+0

Ха-ха, я действительно. Мой умственный компилятор сегодня не должен работать. :) – bcat

3

При добавлении объектов в наборе он внутренне вызывает equals и hashCode методы. Вы должны переопределить эти два метода. Например, я взял один класс bean с name, id, designation, а затем создал и добавил объект employee.

HashSet<Employee> set = new HashSet<Employee>(); 
Employee employee = new Employee(); 
employee.setId(1); 
employee.setName("jagadeesh"); 
employee.setDesignation("programmer"); 
set.add(employee); 
Employee employee2 = new Employee(); 
employee2.setId(1); 
employee2.setName("jagadeesh"); 
employee2.setDesignation("programmer"); 
set.add(employee2); 

set.add() вызовы внутренне equals и hashCode методы. Поэтому вы должны переопределить эти два метода в своем классе bean.

@Override 
public int hashCode(){ 
    StringBuffer buffer = new StringBuffer(); 
    buffer.append(this.name); 
    buffer.append(this.id); 
    buffer.append(this.designation); 
    return buffer.toString().hashCode(); 
} 
@Override 
public boolean equals(Object object){ 
    if (object == null) return false; 
    if (object == this) return true; 
    if (this.getClass() != object.getClass())return false; 
    Employee employee = (Employee)object; 
    if(this.hashCode()== employee.hashCode())return true; 
    return false; 
} 

Здесь мы перекрывая equals() и hashCode(). Когда вы добавляете объект к методу HashSet, он внутренне выполняет итерацию всех объектов и вызывает метод equals. Следовательно, мы переопределяем hashCode, он сравнивает все объекты hashCode с текущим hashCode и возвращает true, если оба они равны, иначе он возвращает false.

Смежные вопросы