Почему ConcurrentHashMap предотвращает нулевые ключи и значения?

JavaDoc ConcurrentHashMap говорит это:

подобно Hashtable но в отличие от HashMapэтот класс не позволяет null использоваться в качестве ключа или значения.

Мой вопрос: почему?

2-й вопрос: почему Hashtable не допускает нулевое значение?

Я использовал много HashMaps для хранения данных. Но при переходе на ConcurrentHashMap у меня несколько раз возникали проблемы из-за исключений NullPointerException.

7 ответов

Решение

От автора ConcurrentHashMap сам (Даг Ли)

Основная причина того, что пустые значения не допускаются в ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps), заключается в том, что неоднозначности, которые могут быть едва допустимыми в непараллельных картах, не могут быть учтены. Основным является то, что если map.get(key) возвращается null, вы не можете определить, соответствует ли ключ явно null против ключ не отображается. На неконкурентной карте вы можете проверить это с помощью map.contains(key), но одновременно, карта могла измениться между вызовами.

Я полагаю, что это, по крайней мере частично, позволит вам объединить containsKey а также get в один звонок. Если карта может содержать нули, нет никакого способа сказать, если get возвращает ноль, потому что не было ключа для этого значения, или просто потому, что значение было нулевым.

Почему это проблема? Потому что нет безопасного способа сделать это самостоятельно. Возьмите следующий код:

if (m.containsKey(k)) {
   return m.get(k);
} else {
   throw new KeyNotPresentException();
}

поскольку m является одновременной картой, ключ k может быть удален между containsKey а также get вызовы, в результате чего этот фрагмент возвращает нулевой, который никогда не был в таблице, а не желаемый KeyNotPresentException,

Обычно вы решаете это путем синхронизации, но с параллельной картой это, конечно, не сработает. Отсюда и подпись get пришлось изменить, и единственный способ сделать это обратно-совместимым способом состоял в том, чтобы запретить пользователю вставлять нулевые значения в первую очередь, и продолжать использовать это в качестве заполнителя для "ключ не найден".

Джош Блох разработал HashMap; Дуг Ли разработал ConcurrentHashMap, Я надеюсь, что это не клевета. На самом деле, я думаю, что проблема заключается в том, что для нулей часто требуется перенос, чтобы реальный нуль мог обозначать неинициализированный. Если клиентскому коду требуются значения NULL, он может оплатить (по общему признанию, небольшую) стоимость упаковки NULL.

Вы не можете синхронизироваться по нулю.

Изменить: это не совсем то, почему в этом случае. Сначала я думал, что происходит что-то необычное с блокировкой на предмет одновременных обновлений или иным образом с помощью монитора объектов, чтобы определить, было ли что-то изменено, но, изучив исходный код, я обнаружил, что был неправ - они блокируются с помощью "сегмента", основанного на битовая маска хеша.

В этом случае я подозреваю, что они сделали это для копирования Hashtable, и я подозреваю, что Hashtable сделал это, потому что в мире реляционных баз данных null!= Null, поэтому использование пустого значения в качестве ключа не имеет смысла.

Я предполагаю, что следующий фрагмент документации API дает хороший совет: "Этот класс полностью совместим с Hashtable в программах, которые полагаются на безопасность потоков, но не на детали синхронизации".

Они, вероятно, просто хотели сделать ConcurrentHashMap полностью совместимы / взаимозаменяемы с Hashtable, И в качестве Hashtable не допускает нулевые ключи и значения..

ConcurrentHashMap является потокобезопасным. Я считаю, что недопущение пустых ключей и значений было частью гарантии, что это потокобезопасно.

Я не думаю, что запрещение нулевого значения является правильным вариантом. Во многих случаях мы хотим поместить ключ с нулевым значением в текущую карту. Однако, используя ConcurrentHashMap, мы не можем этого сделать. Я полагаю, что будущая версия JDK может поддержать это.

Другие вопросы по тегам