ConcurrentHashMap のJavaDocはこう言っています:
Hashtable
に似ていますが、HashMap
とは異なり、このクラスではnotnull
をキーまたは値。
私の質問:なぜですか?
2番目の質問:Hashtableがnullを許可しないのはなぜですか?
データを保存するために多くのHashMapを使用しました。しかし、ConcurrentHashMapに変更すると、NullPointerExceptionsが原因で何度かトラブルに巻き込まれました。
ConcurrentHashMap
の著者自身(Doug Lea) :
ConcurrentMaps(ConcurrentHashMaps、ConcurrentSkipListMaps)でnullが許可されていない主な理由は、非並行マップではほとんど容認できないかもしれないあいまいさを収容できないことです。主なものは、
map.get(key)
がnull
を返す場合、キーがnull
に明示的にマップされているかどうかを検出できないことです。非並行マップでは、map.contains(key)
を介してこれを確認できますが、並行マップでは、呼び出し間でマップが変更された可能性があります。
少なくとも部分的には、containsKey
とget
を1つの呼び出しに結合できると信じています。マップがnullを保持できる場合、その値のキーがなかったため、または値がnullであったために、get
がnullを返しているかどうかを判断する方法はありません。
なぜそれが問題なのですか?自分でそれを行う安全な方法はないからです。次のコードを取得します。
if (m.containsKey(k)) {
return m.get(k);
} else {
throw new KeyNotPresentException();
}
m
は同時マップであるため、containsKey
呼び出しとget
呼び出しの間でキーkが削除され、このスニペットがテーブルにないNullを返すことがあります。目的のKeyNotPresentException
。
通常は同期することでそれを解決しますが、同時マップではもちろん動作しません。したがって、get
の署名を変更する必要があり、下位互換性のある方法でそれを行う唯一の方法は、ユーザーが最初にnull値を挿入しないようにし、それを "key見つかりません"。
Josh Blochが設計したHashMap
; Doug LeaがConcurrentHashMap
を設計しました。それが名誉t損でないことを願っています。実際、問題は、実際のnullが初期化されていないことを表すことができるように、nullはしばしばラップを必要とすることだと思います。クライアントコードがnullを必要とする場合、null自体をラップする(確かに小さい)コストを支払うことができます。
ConcurrentHashMapはスレッドセーフです。 nullキーとnull値を許可しないことは、スレッドセーフであることを確認することの一部だったと思います。
Nullでは同期できません。
編集:この場合、これは正確な理由ではありません。当初は、同時更新に対してロックすることや、何かが変更されたかどうかを検出するためにオブジェクトモニターを使用することで、何か空想があると思っていましたが、 ソースコード を調べると、間違っているように見えます-ハッシュのビットマスクに基づく「セグメント」。
その場合、Hashtableをコピーするために行ったのではないかと思われます。また、リレーショナルデータベースの世界ではnull!= nullなので、キーとしてnullを使用しても意味がないためです。
APIドキュメントの次のスニペットが良いヒントを与えていると思います。「このクラスは、スレッドの安全性に依存しているが同期の詳細には依存していないプログラムでHashtableと完全に相互運用可能です。」
おそらくConcurrentHashMap
をHashtable
と完全に互換性/交換可能にしたかっただけでしょう。 Hashtable
はnullキーと値を許可しないため。