В чем разница между HashSet, LinkedHashSet и TreeSet в Java?

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

Ответ

Все три класса реализуют интерфейс Set (хранят уникальные элементы), но отличаются внутренней структурой, производительностью и порядком элементов.

Критерий HashSet LinkedHashSet TreeSet
Внутренняя структура Хеш-таблица Хеш-таблица + двусвязный список Красно-чёрное дерево
Порядок элементов Не гарантируется Порядок вставки Естественный порядок или порядок заданный Comparator
Производительность
(add, contains, remove)
O(1) в среднем O(1) в среднем,
небольшие накладные расходы
O(log n)
Требования к элементам Должны корректно реализовывать hashCode() и equals() Как у HashSet Должны реализовывать Comparable или передаваться Comparator
Допустимость null Да (один элемент) Да (один элемент) Нет (если используется естественный порядок)

Пример, демонстрирующий разницу в порядке:

Set<Integer> hashSet = new HashSet<>(List.of(3, 1, 4, 2));
System.out.println(hashSet); // Возможный вывод: [1, 2, 3, 4] (порядок неопределён)

Set<Integer> linkedSet = new LinkedHashSet<>(List.of(3, 1, 4, 2));
System.out.println(linkedSet); // Гарантированный вывод: [3, 1, 4, 2] (порядок вставки)

Set<Integer> treeSet = new TreeSet<>(List.of(3, 1, 4, 2));
System.out.println(treeSet); // Гарантированный вывод: [1, 2, 3, 4] (сортировка)

Выбор реализации:

  • HashSet: Для максимальной скорости, когда порядок не важен.
  • LinkedHashSet: Когда нужна скорость HashSet и предсказуемый порядок итерации (например, для кэша LRU).
  • TreeSet: Когда элементы должны быть всегда отсортированы.