← Назад к вопросам

Как хранятся несколько null ключей в Hash коллекциях

1.0 Junior🔥 101 комментариев
#Коллекции#Основы Java

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

# Несколько 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 не может быть несколько?

Причины:

  1. Уникальность ключей — каждый ключ в Map должен быть уникален
  2. null == null — при поиске null всегда найдёт сам себя
  3. Хеш-функция для 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