Друзья, сегодня публикую первую часть подборки вопрос-ответ для собеседования, в ней постарался совместить часть теории и суть ответа:



1. Почему ёмкость HashMap — степень двойки?
HashMap хранит элементы в массиве bucket'ов. Чтобы определить bucket, используется:
index = (n - 1) & hash;
где n — размер массива.
Почему именно степень двойки? Потому что если n = 16:

n = 10000
n - 1 = 01111

Операция hash & 01111 оставляет только младшие 4 бита hash и гарантированно дает индекс от 0 до 15.

Если размер не степень двойки, например 10:

n - 1 = 9 = 01001

маска содержит нули между значащими битами. В результате часть битов hash вообще не участвует в выборе bucket'а, и распределение может стать хуже.
Есть и практический бонус при resize: при увеличении capacity в 2 раза элемент либо остается в том же bucket, либо перемещается на oldCapacity. Не нужно заново вычислять сложное распределение для каждого элемента.

Итого: степень двойки нужна не просто «для быстроты». Она позволяет эффективно использовать биты hash, быстро вычислять индекс через & и дешево выполнять resize.


2. HashMap vs ConcurrentHashMap — потокобезопасность и производительность
HashMap не является thread-safe. Одновременная модификация из нескольких потоков может привести к race condition и некорректному состоянию структуры.
ConcurrentHashMap специально спроектирован для конкурентного доступа. В современной Java он не использует один глобальный lock: применяются CAS и локальная синхронизация отдельных bucket'ов.
Поэтому два потока могут одновременно работать с разными участками Map, не блокируя друг друга.

HashMap
- быстро в одном потоке
- нет thread-safety

ConcurrentHashMap
- немного сложнее
- thread-safe
- лучше масштабируется при конкуренции
При этом ConcurrentHashMap запрещает null как ключ и значение.

3. Как устроен ConcurrentHashMap?
В Java 8+ ConcurrentHashMap уже не использует Segment'ы — это архитектура старых версий.
В основе современной реализации лежит массив Node[].
При вставке:
Если bucket пуст — используется CAS, чтобы атомарно установить первый элемент.
Если bucket уже занят — работа синхронизируется на конкретном bucket, а не на всей Map.
При большом количестве коллизий bucket может превратиться из списка в красно-чёрное дерево.
При resize несколько потоков могут совместно переносить элементы.
Поэтому главная идея ConcurrentHashMap:

минимизировать область блокировки и позволить нескольким потокам работать параллельно.



4. ConcurrentHashMap vs Collections.synchronizedMap()

Collections.synchronizedMap(new HashMap<>());
фактически синхронизирует доступ к Map через общий lock.
Условно:
synchronizedMap
↓
один lock
↓
вся Map

У ConcurrentHashMap блокировка гораздо более локальная, поэтому при большом количестве параллельных операций он масштабируется лучше.
synchronizedMap вполне подходит, если:
- конкуренции мало;
- Map небольшая;
- нужна простая синхронизация.

Для высококонкурентного кода обычно выбирают ConcurrentHashMap.

Важный нюанс
: при итерации synchronizedMap нужно самостоятельно синхронизироваться на Map, если требуется согласованное поведение.

5. TreeMap vs HashMap

HashMap использует hash table: get / put / remove O(1) в среднем
TreeMap основан на красно-чёрном дереве: get / put / remove O(log n)
Зачем тогда нужен TreeMap?
Потому что он поддерживает отсортированный порядок ключей и операции с диапазонами:

firstKey()
lastKey()
higherKey()
lowerKey()
subMap()
headMap()
tailMap()

Поэтому выбор простой:

Нужен быстрый поиск по ключу HashMap



Нужны сортировка ключей и range-запросы TreeMap
Просмотры: 76
Открыть в MAX Сообщить о неточности

Репосты публикации

Публикация канала

Сообщить о неточности

Опишите, что нужно исправить. Если есть источник, добавьте ссылку.

Вы покидаете datool.ru

Ссылка ведёт на внешний сайт. datool.ru не отвечает за его содержимое — проверьте адрес, прежде чем вводить пароли или платёжные данные.

Переход через 10 с

Перейти