web-dev-qa-db-ja.com

ConcurrentHashMapがnullキーとnull値を防ぐのはなぜですか?

ConcurrentHashMap のJavaDocはこう言っています:

Hashtableに似ていますが、HashMapとは異なり、このクラスではnotnullをキーまたは値。

私の質問:なぜですか?

2番目の質問:Hashtableがnullを許可しないのはなぜですか?

データを保存するために多くのHashMapを使用しました。しかし、ConcurrentHashMapに変更すると、NullPointerExceptionsが原因で何度かトラブルに巻き込まれました。

132
Marcel

ConcurrentHashMapの著者自身(Doug Lea)

ConcurrentMaps(ConcurrentHashMaps、ConcurrentSkipListMaps)でnullが許可されていない主な理由は、非並行マップではほとんど容認できないかもしれないあいまいさを収容できないことです。主なものは、map.get(key)nullを返す場合、キーがnullに明示的にマップされているかどうかを検出できないことです。非並行マップでは、map.contains(key)を介してこれを確認できますが、並行マップでは、呼び出し間でマップが変更された可能性があります。

208
Bruno

少なくとも部分的には、containsKeygetを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見つかりません"。

41
Alice Purcell

Josh Blochが設計したHashMap; Doug LeaがConcurrentHashMapを設計しました。それが名誉t損でないことを願っています。実際、問題は、実際のnullが初期化されていないことを表すことができるように、nullはしばしばラップを必要とすることだと思います。クライアントコードがnullを必要とする場合、null自体をラップする(確かに小さい)コストを支払うことができます。

ConcurrentHashMapはスレッドセーフです。 nullキーとnull値を許可しないことは、スレッドセーフであることを確認することの一部だったと思います。

0
Kevin Crowell

Nullでは同期できません。

編集:この場合、これは正確な理由ではありません。当初は、同時更新に対してロックすることや、何かが変更されたかどうかを検出するためにオブジェクトモニターを使用することで、何か空想があると思っていましたが、 ソースコード を調べると、間違っているように見えます-ハッシュのビットマスクに基づく「セグメント」。

その場合、Hashtableをコピーするために行ったのではないかと思われます。また、リレーショナルデータベースの世界ではnull!= nullなので、キーとしてnullを使用しても意味がないためです。

0
Paul Tomblin

APIドキュメントの次のスニペットが良いヒントを与えていると思います。「このクラスは、スレッドの安全性に依存しているが同期の詳細には依存していないプログラムでHashtableと完全に相互運用可能です。」

おそらくConcurrentHashMapHashtableと完全に互換性/交換可能にしたかっただけでしょう。 Hashtableはnullキーと値を許可しないため。

0
Tobias Müller