Java Hashset.contains() создает таинственный результат
Я обычно не кодирую на Java, но в последнее время у меня не было выбора. Возможно, у меня возникло серьезное непонимание того, как правильно использовать HashSet. Таким образом, возможно, что-то, что я сделал, просто неправильно. Однако я благодарен за любую помощь, которую вы могли бы предложить. Итак, актуальная проблема:
В небольшой программе, которую я писал, я генерировал очень похожие объекты, которые при создании имели бы очень специфический id (a string
или в моей последней итерации a 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)
Любые идеи или предложения?
Ответы
Ответ 1
Ваш метод equals
должен принять Object
.
Поскольку вы объявили его как принимающий L
, он становится дополнительной перегрузкой вместо переопределения метода.
Поэтому, когда класс hashSet
вызывает equals
, он разрешает базовый метод Object.equals
. Когда вы вызываете equals
, вы вызываете свою перегрузку, потому что a
и b
объявляются как L
вместо Object
.
Чтобы предотвратить эту проблему в будущем, вы должны добавить @Override
всякий раз, когда вы переопределяете метод.
Таким образом, компилятор предупредит вас, если это не переопределение.
Ответ 2
На самом деле вы не переопределяете Object.equals
; вместо этого вы определяете новый метод с тем же именем, но с разными параметрами. Обратите внимание, что Object.equals
принимает аргумент Object
, тогда как ваш метод equals принимает аргумент L
. Если вы переписываете метод equals для принятия Object
и выполняете необходимую проверку/литье типов на L
во время выполнения, то ваш код работает так, как вы ожидаете.
Кроме того, именно поэтому вы действительно должны использовать аннотации @Override
всякий раз, когда ваш JRE поддерживает их. Таким образом, компилятор будет жаловаться, если вы случайно реализуете новый метод, когда вы собираетесь переопределить существующий.
В качестве примера этот метод equals должен работать правильно. (И, в случае несвязанной заметки, он не будет терпеть неудачу, если сравниваемый объект равен null.)
@Override
public boolean equals(Object other) {
return other != null && other instanceof L && this.l == ((L)other).l;
}
Ответ 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.