ConcurrentHashMap null anahtarları ve değerleri neden engelliyor?


141

JavaDoc ConcurrentHashMapşunları söylüyor:

Gibi Hashtableama aksine HashMap, bu sınıf yok değil izin nullbir anahtar veya değer olarak kullanılmak üzere.

Sorum: Neden?

2. soru: Null'a neden Hashtableizin vermiyor?

Veri depolamak için çok sayıda HashMaps kullandım. Ancak ConcurrentHashMapNullPointerExceptions nedeniyle birkaç kez sorunla karşılaştım.


1
Bence bu son derece sinir bozucu bir tutarsızlık. EnumMap da null değerine izin vermez. Açık anahtarlara izin vermeyen hiçbir teknik sınırlama yoktur. Bir Harita <K, V> için, sadece V-tipi alan null anahtarlar için destek sağlayacaktır (null değeri ve değeri olmayanı ayırmak istiyorsanız muhtemelen başka bir boole alanı).
RAY

6
Daha iyi bir soru "HashMap neden boş anahtar ve boş değerlere izin veriyor?" Sorusudur. Belki de, "Java null'un tüm türlerde yaşamasına neden izin veriyor?", Hatta "Java'nın neden hiç null değeri var?".
Jed Wesley-Smith

Yanıtlar:


220

ConcurrentHashMapKendisinin yazarından (Doug Lea) :

ConcurrentMaps (ConcurrentHashMaps, ConcurrentSkipListMaps) içinde null değerlerine izin verilmemesinin temel nedeni, eşzamanlı olmayan haritalarda zar zor tolere edilebilecek belirsizliklerin giderilememesidir. Birincisi, map.get(key)döndürürse null, anahtarın anahtarla açıkça eşleşip eşleşmediğini algılayamazsınız null. Eşzamanlı olmayan bir haritada bunu kontrol edebilirsiniz map.contains(key), ancak eşzamanlı bir haritada harita aramalar arasında değişmiş olabilir.


7
Teşekkür ederim, ama anahtar olarak boş kalmaya ne dersiniz?
AmitW

2
neden Optional
s'yi

2
@benez Optional, Java 8 özelliğidir ve şu anda mevcut değildir (Java 5). OptionalGerçekten s kullanabilirsiniz .
Bruno

@AmitW, sanırım ans aynı yani belirsizlikler. Örneğin, bir iş parçacığının bir anahtarı null yaptığını ve ona karşı bir değer depoladığını varsayalım. Sonra başka bir iş parçacığı başka bir anahtarı null olarak değiştirdi. İkinci iş parçacığı yeni değer eklemeye çalıştığında, değiştirilir. İkinci iş parçacığı değer almaya çalışırsa, farklı bir anahtarın değerini alır; bu, birincisi tarafından değiştirilir. Böyle bir durumdan kaçınılmalıdır.
Dexter

44

En azından kısmen, bir araya getirmenize containsKeyve gettek bir görüşmeye izin vermenize inanıyorum . Harita null değerlerini tutabilirse, getnull değerini döndürüp döndürmediğini anlamanın bir yolu yoktur , çünkü bu değer için herhangi bir anahtar yoktur veya yalnızca değer null olduğundan.

Bu neden bir sorun? Çünkü bunu kendiniz yapmanın güvenli bir yolu yok. Aşağıdaki kodu alın:

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

Yana mbir eşzamanlı haritasıdır, anahtar k arasına silinebilir containsKeyve getyerine istenenden daha tablodaki asla bir null dönmek için bu pasajı neden aramalar KeyNotPresentException.

Normalde bunu senkronize ederek çözersiniz, ancak eşzamanlı bir harita ile elbette işe yaramaz. Bu nedenle imzasının getdeğişmesi gerekiyordu ve bunu geriye dönük olarak uyumlu bir şekilde yapmanın tek yolu, kullanıcının null değerleri ilk etapta girmesini önlemek ve bunu "anahtar bulunamadı" için yer tutucu olarak kullanmaya devam etmekti.


Yapabilirsin map.getOrDefault(key, NULL_MARKER). Eğer öyleyse null, değerdi null. Geri dönerse NULL_MARKER, değer yoktu.
Oliv

@Oliv Yalnızca Java 8'den itibaren. Bu tür için anlamlı bir boş işaretleyici olmayabilir.
Alice Purcell

@AlicePurcell, "ama tabii ki eşzamanlı bir harita ile elbette işe yaramaz" - neden, ben aynı anda aynı anda senkronize edebilirsiniz - bu yüzden neden işe yaramaz merak. bunu ayrıntılandırabilir misin?
samshers

@samshers Eşzamanlı bir harita üzerinde hiçbir işlem senkronize edilmez, bu nedenle tüm aramaları harici olarak senkronize etmeniz gerekir, bu noktada sadece eşzamanlı bir haritaya sahip olmanın tüm performans avantajını kaybetmekle kalmaz, gelecekteki koruyucular için bir tuzak bırakmış olursunuz doğal olarak eşzamanlı bir haritaya senkronize etmeden güvenli bir şekilde erişmeyi bekler.
Alice Purcell

@AlicePurcell, harika. Teknik olarak mümkün olmasına rağmen, bu kesinlikle bir bakım kabusu olacak ve daha sonraki kullanıcılar tarafından eşzamanlı sürümde senkronize olmaları beklenmeyecek.
samshers

4

Josh Bloch tasarladı HashMap; Doug Lea tasarladı ConcurrentHashMap. Umarım bu iftiralı değildir. Aslında sorun, null'ların genellikle sarılmaya ihtiyaç duymasıdır, böylece gerçek null başlatılmamış olabilir. İstemci kodu null gerektiriyorsa, null'lerin kendisini sarmanın (kuşkusuz küçük) maliyetini ödeyebilir.


2

Bir boşta senkronizasyon yapamazsınız.

Edit: Bu tam olarak bu durumda neden değil. Başlangıçta eşzamanlı güncellemelere karşı şeyleri kilitleme ya da bir şey değiştirilip değiştirilmediğini tespit etmek için Nesne monitörünü kullanarak bir şeylerin olduğunu düşündüm, ancak kaynak kodunu inceledikten sonra yanıldım - bir "segment" kullanarak kilitlendiler karma bitmask.

Bu durumda, Hashtable'ı kopyalamak için yaptıklarından şüpheliyim ve Hashtable'ın bunu yaptığından şüpheliyim, çünkü ilişkisel veritabanı dünyasında null! = Null, bu yüzden bir anahtar olarak null kullanmanın anlamı yoktur.


Ha? Bir Haritanın anahtarları ve değerleri üzerinde senkronizasyon yapılmaz. Bu hiç mantıklı değil.
Tobias Müller

Başka türlü kilitleme işlemleri var. "Eşzamanlı" yapan da budur. Bunu yapmak için, asılmak için bir nesneye ihtiyacı vardır.
Paul Tomblin

2
Neden dahili olarak null değerleri senkronize etmek için kullanılabilecek belirli bir Nesne yok? örneğin "private Object NULL = new Object ();". Sanırım bunu daha önce görmüştüm ...
Marcel

Başka ne tür kilitlemeler demek istiyorsun?
Tobias Müller

Aslında şimdi gee.cs.oswego.edu/dl/classes/EDU/oswego/cs/dl/util/concurrent/… kaynak koduna baktığımda bu konuda ciddi şüphelerim var. Görünüşe göre tek tek öğeleri kilitlememek yerine segment kilitleme kullanır.
Paul Tomblin

0

ConcurrentHashMap iş parçacığı için güvenlidir. Boş anahtarlara ve değerlere izin vermemenin, iş parçacığı açısından güvenli olduğundan emin olmanın bir parçası olduğuna inanıyorum.


0

API belgelerinin aşağıdaki snippet'inin iyi bir ipucu verdiğini tahmin ediyorum: "Bu sınıf, iplik güvenliğine dayanan ancak senkronizasyon ayrıntılarına bağlı olmayan programlarda Hashtable ile tamamen birlikte çalışabilir."

Muhtemelen sadece ConcurrentHashMaptamamen uyumlu / değiştirilebilir olmasını istediler Hashtable. Ve Hashtableboş anahtarlara ve değerlere izin vermez.


2
Ve Hashtable neden null değerini desteklemiyor?
Marcel

Koduna baktığımda, Hashtable'ın null değerlere izin vermediğinin açık bir nedenini görmüyorum. Belki de sınıf yaratıldığında sadece bir API kararıdır ?! HashMap, Hashtable'ın içermediği null durumda dahili olarak bazı özel işlemlere sahiptir. (Her zaman NullPointerException atar.)
Tobias Müller

-2

Boş değere izin vermemek doğru bir seçenek olduğunu düşünmüyorum. Çoğu durumda, geçerli haritaya null değerli bir anahtar koymak istiyoruz. Ancak, ConcurrentHashMap kullanarak bunu yapamayız. JDK'nın gelecek sürümünün bunu destekleyebileceğini öneriyorum.


1
Javachampion için yarışmayı düşündün mü?
BlackBishop

Anahtarlarınızda null benzeri davranışlar istiyorsanız, İsteğe Bağlı seçeneğini kullanın.
Alice Purcell
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.