JAVA_FILLTHEGAPS Telegram 478
Java 20: новый костыль в HashMap

На вопрос выше логично ответить new HashMap<>(1000). Сначала расскажу, почему этот ответ не походит. Потом наглядно покажу нарушение инкапсуляции, и что за костыль добавили в Java 20.

Итак, что происходит внутри HashMap?

Ячейки хэш-таблицы (или бакеты) хранятся в переменной Node[] table как простой массив.

При вызове new HashMap() без параметров создаётся 16 бакетов. Если передать параметр с размером, то берётся ближайшая степень двойки.

new HashMap(1000) создаёт массив из 1024 элементов.

Кажется, что всё в порядке, но мы забыли про ребалансировку😒

HashMap хорошо работает, когда в каждом бакете 0 или 1 элемент. Тогда скорость поиска и добавления будет той самой О(1).

Когда элементов становится больше, растёт шанс, что в один бакет попадёт несколько элементов. Поэтому в определённый момент HashMap удваивает количество бакетов и перераспределяет элементы. За момент, когда пора начать эту операцию, отвечает поле threshold.

Его первое значение считается как [планируемое число элементов * 0.75], т.е когда HashMap заполнен на 3/4. При удвоении числа бакетов threshold тоже удваивается.

И смотрите, что получается:

🔹 Мы хотим добавить в мэп 1000 элементов и вызываем конструктор с подходящим параметром initialCapacity:
new HashMap(1000);

🔹 Создаётся 1024 бакета (ближайшая степень двойки)
🔹 Рассчитывается threshold: 1024*0,75 = 768
🔹 Добавляется 768 элементов
🔹 Приходит 769 элемент, начинается ребалансировка:
▫️ количество бакетов удваивается, теперь их 2048
▫️ текущие элементы распределяются между ними
▫️ новый threshold удваивается, теперь это 1538

Что получилось: мы пообещали добавить в HashMap 1000 элементов. Сдержали обещание, но перестройка мэп всё равно произошла.

Чтобы HashMap работал оптимально, нужно учесть ребалансировку и передать в конструктор, например, 1500. Надо знать детали реализации, чтобы получить то, что хотим.

И это образцовое нарушение инкапсуляции🤌

В java 20 в HashMap добавили костыльный метод, который исправляет ситуацию:

HashMap.newHashMap(1000);

Внутри произойдёт вычисление 1000 / 0.75 = 1334, в итоге создаётся 2048 бакетов.

Почему это костыль? Потому что исходная проблема не решается. Жизнь пользователя не становится легче, ему нужно запомнить "чтобы задать размер мэп — не пользуйся конструктором, пользуйся специальным методом".

Хороший API — понятный, удобный и дружелюбный. Пользователю легко выбрать нужный метод, все параметры хорошо описаны в документации. Когнитивная нагрузка при использовании минимальна, нет подводных камней и обходных путей. Для собеседований сложно придумать вопрос с подвохом🙂

Важные заметки:

🔸 В JDK много образцового кода, и я рекомендую изучать исходники как можно чаще. HashMap — неприятное исключение

🔸 В ConcurrentHashMap всё хорошо. new ConcurrentHashMap(1000) сразу создаёт 2048 бакетов и не занимается лишними балансировками



tgoop.com/java_fillthegaps/478
Create:
Last Update:

Java 20: новый костыль в HashMap

На вопрос выше логично ответить new HashMap<>(1000). Сначала расскажу, почему этот ответ не походит. Потом наглядно покажу нарушение инкапсуляции, и что за костыль добавили в Java 20.

Итак, что происходит внутри HashMap?

Ячейки хэш-таблицы (или бакеты) хранятся в переменной Node[] table как простой массив.

При вызове new HashMap() без параметров создаётся 16 бакетов. Если передать параметр с размером, то берётся ближайшая степень двойки.

new HashMap(1000) создаёт массив из 1024 элементов.

Кажется, что всё в порядке, но мы забыли про ребалансировку😒

HashMap хорошо работает, когда в каждом бакете 0 или 1 элемент. Тогда скорость поиска и добавления будет той самой О(1).

Когда элементов становится больше, растёт шанс, что в один бакет попадёт несколько элементов. Поэтому в определённый момент HashMap удваивает количество бакетов и перераспределяет элементы. За момент, когда пора начать эту операцию, отвечает поле threshold.

Его первое значение считается как [планируемое число элементов * 0.75], т.е когда HashMap заполнен на 3/4. При удвоении числа бакетов threshold тоже удваивается.

И смотрите, что получается:

🔹 Мы хотим добавить в мэп 1000 элементов и вызываем конструктор с подходящим параметром initialCapacity:
new HashMap(1000);

🔹 Создаётся 1024 бакета (ближайшая степень двойки)
🔹 Рассчитывается threshold: 1024*0,75 = 768
🔹 Добавляется 768 элементов
🔹 Приходит 769 элемент, начинается ребалансировка:
▫️ количество бакетов удваивается, теперь их 2048
▫️ текущие элементы распределяются между ними
▫️ новый threshold удваивается, теперь это 1538

Что получилось: мы пообещали добавить в HashMap 1000 элементов. Сдержали обещание, но перестройка мэп всё равно произошла.

Чтобы HashMap работал оптимально, нужно учесть ребалансировку и передать в конструктор, например, 1500. Надо знать детали реализации, чтобы получить то, что хотим.

И это образцовое нарушение инкапсуляции🤌

В java 20 в HashMap добавили костыльный метод, который исправляет ситуацию:

HashMap.newHashMap(1000);

Внутри произойдёт вычисление 1000 / 0.75 = 1334, в итоге создаётся 2048 бакетов.

Почему это костыль? Потому что исходная проблема не решается. Жизнь пользователя не становится легче, ему нужно запомнить "чтобы задать размер мэп — не пользуйся конструктором, пользуйся специальным методом".

Хороший API — понятный, удобный и дружелюбный. Пользователю легко выбрать нужный метод, все параметры хорошо описаны в документации. Когнитивная нагрузка при использовании минимальна, нет подводных камней и обходных путей. Для собеседований сложно придумать вопрос с подвохом🙂

Важные заметки:

🔸 В JDK много образцового кода, и я рекомендую изучать исходники как можно чаще. HashMap — неприятное исключение

🔸 В ConcurrentHashMap всё хорошо. new ConcurrentHashMap(1000) сразу создаёт 2048 бакетов и не занимается лишними балансировками

BY Java: fill the gaps


Share with your friend now:
tgoop.com/java_fillthegaps/478

View MORE
Open in Telegram


Telegram News

Date: |

Earlier, crypto enthusiasts had created a self-described “meme app” dubbed “gm” app wherein users would greet each other with “gm” or “good morning” messages. However, in September 2021, the gm app was down after a hacker reportedly gained access to the user data. 4How to customize a Telegram channel? Users are more open to new information on workdays rather than weekends. End-to-end encryption is an important feature in messaging, as it's the first step in protecting users from surveillance. As the broader market downturn continues, yelling online has become the crypto trader’s latest coping mechanism after the rise of Goblintown Ethereum NFTs at the end of May and beginning of June, where holders made incoherent groaning sounds and role-played as urine-loving goblin creatures in late-night Twitter Spaces.
from us


Telegram Java: fill the gaps
FROM American