Можно ли хранить 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]]