Ответ
Все три класса реализуют интерфейс 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: Когда элементы должны быть всегда отсортированы.