Java: HashSet, что такое концепция сравнения?

Исходя из мира C++, я нахожу чтение документации HashSet несколько трудным:

В C++ вы должны иметь:

что в свою очередь указывает на:

Что делает очевидным требование к типу элемента, обрабатываемого std::set, Мой вопрос: каковы требования к типу (E) элементов, поддерживаемых Set на яве?

Вот короткий пример, который я не понимаю:

import gdcm.Tag;
import java.util.Set;
import java.util.HashSet;

public class TestTag
{
  public static void main(String[] args) throws Exception
    {
      Tag t1 = new Tag(0x8,0x8);
      Tag t2 = new Tag(0x8,0x8);
      if( t1 == t2 )
        throw new Exception("Instances are identical" );
      if( !t1.equals(t2) )
        throw new Exception("Instances are different" );
      if( t1.hashCode() != t2.hashCode() )
        throw new Exception("hashCodes are different" );
      Set<Tag> s = new HashSet<Tag>();
      s.add(t1);
      s.add(t2);
      if( s.size() != 1 )
        throw new Exception("Invalid size: " + s.size() );
    }
}

Приведенный выше простой код завершается ошибкой с:

Exception in thread "main" java.lang.Exception: Invalid size: 2 at TestTag.main(TestTag.java:42)

Из моего прочтения документации для Set необходимо реализовать только оператор equals:

Чего мне не хватает в документации?

4 ответа

Решение

Я просто попытался воспроизвести вашу проблему, и, возможно, вы просто неправильно переопределили equals и / или hashSet.

Посмотрите на мою неправильную реализацию тега:

public class Tag {

private int x, y;

public Tag(int x, int y) {
    this.x = x;
    this.y = y;
}

public boolean equals(Tag tag) {
    if (x != tag.x) return false;
    return y == tag.y;
}

@Override
public int hashCode() {
    int result = x;
    result = 31 * result + y;
    return result;
}
}

Выглядит вполне нормально, не так ли? Но проблема в том, что я на самом деле не переопределяю правильный метод equals, я перегрузил его своей собственной реализацией.

Для корректной работы equals должен выглядеть так:

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Tag tag = (Tag) o;

    if (x != tag.x) return false;
    return y == tag.y;
}

Чего мне не хватает в документации?

Вы смотрите не на ту часть документации.

C++ set является "отсортированным набором уникальных объектов" и "обычно реализуется как красно-черные деревья".

В Java Set это более абстрактная концепция (это интерфейс, а не класс) с несколькими реализациями, в частности, HashSetи TreeSet (игнорирование параллельных реализаций).

Как вы можете догадаться по названию, Java TreeSet является эквивалентом C++ set,

Что касается требований, HashSet использует hashCode() а также equals() методы. Они определены на Object класс, и должен быть переопределен на классах, которые должны быть в HashSet или как ключи в HashMap,

За TreeSet и ключи TreeMap, у вас есть два варианта: предоставить Comparator при создании TreeSet (аналогично C++) или объекты должны реализовывать Comparable интерфейс.

Я предполагаю, что это была просто комбинация неудачи и неправильного понимания требования HashSet. Спасибо @christophe за помощь, я понял проблему, когда попытался добавить в свой сгенерированный Swig класс Tag.java:

@Override
public boolean equals(Object o) {
}

Я получил следующее сообщение об ошибке:

gdcm/Tag.java:78: error: method does not override or implement a method from a supertype
  @Override
  ^
1 error
1 warning

Что означало, что моя ошибка была просто:

  • Во-первых, у меня была неправильная подпись: boolean equals(Object o) знак равно boolean equals(Tag t)

Намек был просто использовать @Override ключевое слово.


Для тех, кто запрашивает исходный код, код Java генерируется с помощью swig. Оригинальный код C++ находится здесь:

Размер равен 2 (не равен 1), поэтому он генерирует исключение (ваша последняя строка кода). Изменить: Возможно, я не понял тебя раньше. Как правило, тип E - это то, что вы выбираете. Например: положить объект или "?" Заставит вас поставить любой тип. В вашем случае вы выбираете Tag как тип E, поэтому он должен быть содержимым набора.

Другие вопросы по тегам