Ответ
HashSet использует хеширование для обеспечения константного времени выполнения O(1) в среднем для основных операций: add(), remove() и contains(). В основе HashSet лежит HashMap, где элементы множества являются ключами.
Принцип работы:
- При добавлении элемента вызывается его метод
hashCode(). - На основе хеш-кода вычисляется индекс корзины (bucket) в внутреннем массиве.
- Элемент помещается в эту корзину. Это позволяет мгновенно локализовать поиск.
Пример и визуализация:
Set<String> names = new HashSet<>();
names.add("Alice"); // hashCode("Alice") -> определяет bucket 3
names.add("Bob"); // hashCode("Bob") -> определяет bucket 1
names.contains("Alice"); // Быстрый поиск только в bucket 3
Критические аспекты для эффективности:
- Качество
hashCode(): Плохая хеш-функция (частые коллизии) превращает поиск в O(n) внутри корзины. - Вместимость и коэффициент загрузки (Load Factor, по умолчанию 0.75): При превышении
емкость * loadFactorпроисходит удвоение размера внутреннего массива (rehashing), что дорого, но необходимо для поддержания скорости. - Разрешение коллизий: В Java 8+ при длине цепочки > 8 корзина преобразуется из связного списка в сбалансированное дерево (TreeNode), что гарантирует O(log n) даже при плохих хешах.
Без хеширования HashSet выродился бы в LinkedList с временем поиска O(n).