Ответ
Средняя сложность O(1) (амортизированная константная) достигается благодаря хеш-функции, которая равномерно распределяет ключи по «корзинам» (buckets), и структуре данных внутри корзины.
Как работает HashMap (упрощенно):
- Вычисление индекса: По хеш-коду ключа вычисляется индекс корзины:
index = hash(key) & (n-1). - Прямой доступ: По индексу происходит мгновенный доступ к нужной корзине.
- Разрешение коллизий: Если в одной корзине оказывается несколько элементов (коллизия), в Java 8+ они хранятся как сбалансированное дерево (TreeNodes) при большом количестве, что дает
O(log n)внутри корзины, или как списокO(n). При хорошей хеш-функции и правильном размере таблицы корзины остаются небольшими.
Пример, иллюстрирующий принцип:
HashMap<String, Integer> map = new HashMap<>();
map.put("key1", 100); // ~O(1): вычисляем хеш, находим корзину, кладем значение.
Integer val = map.get("key1"); // ~O(1): вычисляем тот же хеш, сразу идем в ту же корзину.
Условия для O(1):
- Качественная хеш-функция (в
String,Integerона уже реализована), дающая равномерное распределение. - Отсутствие перегрузки: Коэффициент загрузки (load factor, по умолчанию 0.75) контролирует своевременное увеличение количества корзин (rehashing), чтобы цепочки в них оставались короткими.
Важно: В худшем случае (все ключи попали в одну корзину из-за плохой хеш-функции) сложность деградирует до O(n) или O(log n). Поэтому O(1) — это средняя, а не гарантированная сложность.