Как TreeSet проверяет уникальность элементов?

«Как TreeSet проверяет уникальность элементов?» — вопрос из категории Алгоритмы и структуры данных, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

TreeSet определяет уникальность и порядок элементов исключительно на основе компаратора, а не методов equals() и hashCode(), как это делает HashSet.

Механизм проверки:

  • При добавлении элемента TreeSet использует либо:
    1. Естественный порядок: метод compareTo() интерфейса Comparable.
    2. Заданный компаратор: метод compare() объекта Comparator, переданного в конструктор.
  • Если результат сравнения нового элемента с существующим равен 0, элементы считаются эквивалентными, и новый элемент не добавляется.

Пример:

// Использование естественного порядка (String implements Comparable<String>)
TreeSet<String> set = new TreeSet<>();
set.add("apple");
set.add("banana");
boolean added = set.add("apple"); // compareTo("apple", "apple") == 0
System.out.println(added); // false
System.out.println(set); // [apple, banana] - порядок отсортирован

Критически важное замечание: TreeSet нарушает контракт Set, если compareTo()/compare() не согласован с equals(). Элементы, которые equals() считает разными, но компаратор считает равными (возвращает 0), не будут добавлены в набор.

// Проблемный пример: несогласованность compareTo() и equals()
class Item implements Comparable<Item> {
    int id;
    String name;
    // equals() сравнивает только id
    // compareTo() сравнивает только name
}
TreeSet<Item> set = new TreeSet<>();
set.add(new Item(1, "Apple"));
// Объект с id=2, но name="Apple" НЕ будет добавлен,
// хотя equals() сказал бы, что они разные!

Вывод: Для корректной работы TreeSet необходимо, чтобы compareTo() был логически согласован с equals(). В противном случае используйте HashSet.