Равно и сравнимо с множествами

Я разместил код здесь, который правильно решил проблему автора. OP хотел удалить дубликаты и поставить определенные специальные элементы в начало списка. Я использовал TreeSet со специальным классом Comparable, который обертывал Locale, с которым они работали, чтобы достичь того, чего они хотели.

Затем я подумал... как и вы... что я устранял дубликаты, возвращая 0 из метода compareTo, а не возвращая true из реализации equals, как нужно было бы сделать, чтобы правильно указать дубликат в Set ( из определения Set).

У меня нет возражений против использования этого метода, но использую ли я то, что можно считать недокументированной функцией? Могу ли я с уверенностью предположить, что такие вещи в будущем будут продолжать работать?


person OldCurmudgeon    schedule 06.10.2012    source источник
comment
Как отмечает г-н Нуркевич, это специальное поведение, поэтому оно безопасно. Я согласен, что это удивительно, хотя!   -  person Tom Anderson    schedule 06.10.2012


Ответы (1)


Кажется, это довольно хорошо задокументировано в JavaDoc от TreeSet< /a> (выделено жирным шрифтом):

Обратите внимание, что порядок, поддерживаемый набором (независимо от того, предоставлен явный компаратор или нет), должен соответствовать равенству, если он должен правильно реализовать интерфейс Set. (См. Comparable или Comparator для точного определения согласованности с равными.) Это так, потому что интерфейс Set определен в терминах операции equals, но экземпляр TreeSet выполняет все сравнения элементов, используя свой метод compareTo (или сравнение). , так что два элемента, которые считаются равными с помощью этого метода, равны с точки зрения множества. Поведение множества четко определено, даже если его порядок несовместим с равными; он просто не соблюдает общий контракт интерфейса Set.

Вот пример только (?) класса JDK, который реализует Comparable, но не соответствует equals():

Set<BigDecimal> decimals = new HashSet<BigDecimal>();
decimals.add(new BigDecimal("42"));
decimals.add(new BigDecimal("42.0"));
decimals.add(new BigDecimal("42.00"));
System.out.println(decimals);

decimals в конце имеют три значения, потому что 42, 42.0 и 42.00 не равны в отношении equals(). Но если вы замените HashSet на TreeSet, результирующий набор будет содержать только 1 элемент (42, который оказался первым добавленным), так как все они считаются равными при сравнении с использованием BigDecimal.compareTo().

Это показывает, что TreeSet в каком-то смысле "сломан" при использовании типов, несовместимых с equals(). Он по-прежнему работает правильно, и все операции четко определены - он просто не подчиняется контракту класса Set - если два класса не являются equal(), они не считаются дубликатами.

Смотрите также

person Tomasz Nurkiewicz    schedule 06.10.2012
comment
Интересно, что такого комментария нет в документах ConcurrentSkipListSet. - person OldCurmudgeon; 06.10.2012
comment
@OldCurmudgeon На самом деле это также указано в javadoc SortedSet. (и TreeSet, и ConcurrentSkipListSet реализуют этот интерфейс). - person assylias; 06.10.2012