Невозможно удалить или найти объект в Java CopyOnWriteArraySet - PullRequest
6 голосов
/ 08 июня 2011

Я использую CopyOnWriteArraySet для хранения одного экземпляра пользовательского класса, который выглядит следующим образом:

public class MyClass{
 String _name;

 public MyClass(String name){
  _name = name;
 }

 @Override
 public int hashCode(){
  return _name.hashCode();
 }

 @Override
 public boolean equals(Object obj){
  if (obj == this) return true;
  if ((obj instanceof MyClass) == false) return false;
  MyClass otherObject = (MyClass) obj;
  return _name.equals(otherObject._name);
 }

 @Override
 public String toString(){
  return _name;
 }
}

Когда я печатаю комплект, все выглядит нормально:

MyClass theObject = new MyClass("Object 1");
CopyOnWriteArraySet<MyClass> theSet = new CopyOnWriteArraySet();
theSet.add(theObject);

for (MyClass tmp : theSet){
 System.out.println(tmp.toString());
}

Результат:

Объект 1

Итак, очевидно, что объект находится в наборе.

Теперь я хочу удалить объект из набора:

theSet.remove(theObject);

Затем я снова печатаю содержимое комплекта. Результат:

Объект 1

Очень странно. Итак, я попробовал это:

System.out.println(String.valueOf(theSet.contains(theObject)));

Результат:

ложь

Очевидно, что набор не может найти theObject, хотя он есть. Итак, я подумал, что-то не так с методом equals(). Таким образом, я изменил переопределения методов equals() и hashCode(), добавив консольную печать в первую строку каждой функции:

 @Override
 public int hashCode(){
  System.out.println("hashCode() called");
  return _name.hashCode();
 }

 @Override
 public boolean equals(Object obj){
  System.out.println("equals() called");
  if (obj == this) return true;
  if ((obj instanceof MyClass) == false) return false;
  MyClass otherObject = (MyClass) obj;
  return _name.equals(otherObject.name);
 }

Затем я снова звоню:

theSet.remove(theObject);

Результат:

hashCode () называется

Итак, метод equals() вообще не вызывается?

Может кто-нибудь объяснить, что там происходит?

Я уже пытался сравнить хэш-коды theObject и экземпляр внутри набора, и они оба равны.

Ответы [ 3 ]

1 голос
/ 08 июня 2011

Странно .. я проверил ваши коды. И это хорошо работает в моей среде. И операция удаления не вызывает hashCode (), а вызывает equals (). JDK, что я использовал, это 1.6.0_23.

0 голосов
/ 08 июня 2011

Я нашел причину проблемы.

Я использую Hibernate, который создает собственный экземпляр org.hibernate.collection.PersistentSet, который заменил мой CopyOnWriteArraySet!

Тот факт, что .contains () и .remove () не работали, был ошибкой в ​​Hibernate: http://opensource.atlassian.com/projects/hibernate/browse/HHH-3799

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

Примечание: Это может быть не лучшим решением для всех случаев. Для меня это сработало. В приведенной выше ссылке описано несколько обходных путей.

0 голосов
/ 08 июня 2011

HashSet использует hashCode, однако CopyOnWriteArraySet не является HashSet (как и TreeSet) и не вызывает hashCode ().Если вызывается hashCode, вы не используете эту коллекцию.


Это очень странно, потому что я не могу воспроизвести вашу проблему.

MyClass theObject = new MyClass("Object 1");
CopyOnWriteArrayList<MyClass> theSet = new CopyOnWriteArrayList();
// OR
CopyOnWriteArraySet<MyClass> theSet = new CopyOnWriteArraySet();

theSet.add(theObject);
System.out.println("After add.");
System.out.println(theSet);

theSet.remove(theObject);
System.out.println("\nAfter remove");
System.out.println(theSet);

печатает

After add.
[Object 1]

After remove
[]

Даже когда я изменяю hashCode на

public int hashCode() {
    throw new UnsupportedOperationException();
}

он получает тот же результат, потому что эти классы не используют hashCode () (кроме метода hashCode ())

...