Как хранятся несколько null ключей в Hash коллекциях
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
# Несколько null ключей в Hash коллекциях Java
Вопрос о том, как хранятся null ключи в Hash коллекциях, часто возникает у разработчиков. Ответ зависит от типа коллекции и важен для понимания её поведения.
HashMap: Один null ключ
HashMap допускает только ОДИН null ключ. Если попытаться добавить ещё один null, первый будет перезаписан:
Map<String, Integer> map = new HashMap<>();
map.put(null, 1);
map.put(null, 2); // Перезапишет первое значение
System.out.println(map.size()); // 1
System.out.println(map.get(null)); // 2
Danach map содержит только один null ключ со значением 2.
Внутренняя реализация HashMap
Мапа хранит null ключ отдельно, а не в обычном хеш-массиве:
public class HashMap<K, V> extends AbstractMap<K, V> {
// ... другой код ...
Node<K, V> newNode(int hash, K key, V value, Node<K, V> next) {
return new Node<>(hash, key, value, next);
}
public V put(K key, V value) {
if (key == null) {
// Специальная обработка для null
return putForNullKey(value);
}
// ... остальной код ...
}
private V putForNullKey(V value) {
// Null ключ всегда хранится в bucket 0
Node<K, V> p = table[0];
for (Node<K, V> e = p; e != null; e = e.next) {
if (e.key == null) {
V oldValue = e.value;
e.value = value;
return oldValue;
}
}
// ... добавление нового null ключа ...
return null;
}
}
Null ключ всегда хранится в первом bucket (индекс 0) таблицы хеширования.
Практический пример
Map<String, String> map = new HashMap<>();
map.put(null, "первый null");
map.put(null, "второй null"); // Перезапишет первый
map.put("key1", "value1");
map.put("key2", "value2");
map.put(null, "третий null"); // Перезапишет второй
System.out.println(map.size()); // 3 (не 5!)
System.out.println(map.get(null)); // "третий null"
for (Map.Entry<String, String> entry : map.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}
// Вывод:
// null -> третий null
// key1 -> value1
// key2 -> value2
Почему null не может быть несколько?
Причины:
- Уникальность ключей — каждый ключ в Map должен быть уникален
- null == null — при поиске null всегда найдёт сам себя
- Хеш-функция для null — hashCode() нельзя вызвать на null, поэтому обрабатывается отдельно
// Так как null.hashCode() выбросит исключение,
// HashMap обрабатывает null специально
if (key == null) {
// Специальная ветка, не вызывает hashCode()
}
TreeMap: null может вызвать исключение
TreeMap не поддерживает null ключи (если компаратор требует сравнения):
Map<String, Integer> treeMap = new TreeMap<>();
treeMap.put("key1", 1);
treeMap.put(null, 2); // NullPointerException!
ТреMap использует сравнение объектов (compareTo), а null нельзя сравнивать.
LinkedHashMap: Один null ключ
LinkedHashMap также допускает только ОДИН null ключ (наследуется от HashMap):
Map<String, Integer> linkedMap = new LinkedHashMap<>();
linkedMap.put(null, 10);
linkedMap.put(null, 20); // Перезапишет
System.out.println(linkedMap.get(null)); // 20
Порядок вставки сохраняется.
ConcurrentHashMap: Запрещены null ключи
ConcurrentHashMap вообще не позволяет null ключи:
ConcurrentHashMap<String, Integer> concMap = new ConcurrentHashMap<>();
concMap.put(null, 1); // NullPointerException!
Это сделано для потокобезопасности и производительности.
Обработка значений (не ключей)
Основные коллекции допускают несколько null значений:
Map<String, String> map = new HashMap<>();
map.put("key1", null);
map.put("key2", null);
map.put("key3", null);
System.out.println(map.size()); // 3 (три разных null значения!)
Это нормально, потому что null — это просто значение, не ключ.
Best Practices
✅ Правильно:
// Проверяй перед использованием null ключа
Map<String, Integer> map = new HashMap<>();
if (someKey != null) {
map.put(someKey, value);
} else {
// Обработка null ключа отдельно
handleNullKey(value);
}
❌ Неправильно:
// Полагаться на null ключи без понимания их поведения
map.put(null, value1);
map.put(null, value2); // Первое значение потеряется!
Выводы
- HashMap и LinkedHashMap: только ОДИН null ключ (остальные перезаписывают его)
- TreeMap: null ключи запрещены (NullPointerException)
- ConcurrentHashMap: null ключи запрещены
- Значения: null значений может быть неограниченное количество
- Рекомендация: избегай null ключей, используй специальные константы или структуры вместо null