Чи можна зберігати null ключ в HashMap?
При put(null, value):
🟢 Junior Level
Так, HashMap дозволяє використовувати один null в якості ключа. Якщо спробувати додати другий null-ключ, значення просто перезапишеться.
Приклад:
HashMap<Object, String> map = new HashMap<>();
map.put(null, "First");
map.put(null, "Second"); // Перезаписує
System.out.println(map.get(null)); // "Second"
System.out.println(map.size()); // 1 (лише один null-ключ)
Як це працює: HashMap спеціально обробляє null — для нього hash(null) = 0, і він завжди потрапляє в перший бакет (table[0]).
Інші Map:
ConcurrentHashMap— ні (кидає NullPointerException)TreeMap— ні (потрібно порівнювати ключі)Hashtable— ні (стара реалізація)
🟡 Middle Level
Технічна реалізація
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
При put(null, value):
hash(null)повертає 0index = (n - 1) & 0 = 0- Елемент кладеться в
table[0]
Порівняння реалізацій
| Реалізація | Null-ключ | Null-значення |
|---|---|---|
| HashMap | Так (1) | Так (багато) |
| LinkedHashMap | Так (1) | Так |
| TreeMap | Ні | Так |
| ConcurrentHashMap | Ні | Ні |
| Hashtable | Ні | Ні |
Чому це небезпечно?
- Неочевидність — null-ключ може бути багом, який складно виявити
- Неявний null-ключ може викликати NPE в коді, який не очікує null серед ключів (наприклад,
key.length()без перевірки на null) - Несумісність — заміна на ConcurrentHashMap зламає код
Best Practices
- Намагайтеся не використовувати null як ключ
- Використовуйте патерн Null Object або константу:
static final Object EMPTY_KEY = new Object(); map.put(EMPTY_KEY, defaultValue);
Типові помилки
- Випадковий null — результат методу повернув null, і ви кладете як ключ
- Ітерація без перевірки — NPE при використанні ключа
🔴 Senior Level
Internal Mechanics
Null-ключ обробляється окремо в putVal:
if (key == null)
hash = 0;
else
hash = spread(key.hashCode());
При колізіях в бакеті 0:
// Порівняння: (p.key == key || (key != null && key.equals(p.key)))
// Для null: p.key == null → true (порівняння за посиланням)
Null порівнюється лише через == — equals() не викликається.
Security Implications
Null-ключ як вектор атаки:
- При десеріалізації JSON/XML null-ключ може прийти з невалідного вхідного документа. Обробіть це явно, щоб уникнути NPE в коді, який не очікує null серед ключів.
Why ConcurrentHashMap Forbids Null?
Ambiguity в багатопотоковості:
// В ConcurrentHashMap:
// get(key) = null однозначно = ключа немає
// Не потрібен другий виклик containsKey()
В HashMap з null-ключами:
V val = map.get(key);
if (val == null) {
// Ключа немає? Або значення = null?
if (!map.containsKey(key)) {
// TOCTOU race в багатопотоковості!
}
}
Alternative Patterns
Null Object Pattern:
enum SpecialKey { NONE }
map.put(SpecialKey.NONE, defaultValue);
Optional:
Map<Optional<String>, Value> map; // Але це overkill
Production Diagnostics
При налагодженні null-ключів:
- Null-ключ невидимий в деяких профайлерах
- Heap dump покаже Entry з
key = null - В логах:
null=SomeValue— може виглядати як баг форматування
🎯 Шпаргалка для інтерв’ю
Обов’язково знати:
- HashMap допускає ОДИН null-ключ, hash(null) = 0 → table[0]
- При повторному put(null, …) значення перезаписується
- null порівнюється через ==, equals() не викликається
- ConcurrentHashMap/TreeMap/Hashtable забороняють null-ключи (NPE)
- При десеріалізації JSON/XML null-ключ може прийти з невалідного документа
- Null Object Pattern: використовувати константу EMPTY_KEY замість null
Часті уточнюючі запитання:
- Чому ConcurrentHashMap забороняє null-ключ? — ambiguity: get=null = немає ключа чи значення=null?
- Скільки null-ключів можна? — рівно один, другий перезаписує
- Чим небезпечний null-ключ? — NPE в коді, який не очікує null серед ключів
- Як захиститися? — Null Object Pattern, Optional, або явна валідація на вході
Червоні прапорці (НЕ говорити):
- «HashMap не допускає null-ключі» — допускає, один
- «null-ключ = баг» — це допустима поведінка, хоч і не рекомендована
- «ConcurrentHashMap допускає null-ключ» — ні, кидає NPE
Пов’язані теми:
- [[25. Чи можна зберігати null значення в HashMap]]
- [[14. Які вимоги пред’являються до ключа HashMap]]
- [[23. Що таке ConcurrentHashMap і чим він відрізняється від HashMap]]