JDKドキュメントで指定されているように、Hashtableはnullキーまたは値を許可しません。HashMapは、1つのnullキーと任意の数のnull値を許可します。どうしてこれなの?
10 に答える
ハッシュテーブルは古いクラスであり、その使用は一般的に推奨されていません。おそらく、彼らはnullキー、さらに重要なことにnull値の必要性を認識し、それをHashMap実装に追加しました。
HashMapはより新しく、より高度な機能を備えています。これは基本的にHashtable機能の単なる改善です。HashMapが作成されたとき、null値をキーとして処理し、それらを特殊なケースとして処理するように特別に設計されました。
編集
Hashtable
JavaDocから:
ハッシュテーブルからオブジェクトを正常に格納および取得するには、キーとして使用されるオブジェクトがhashCodeメソッドとequalsメソッドを実装する必要があります。
はオブジェクトではないためnull
、呼び出すこともオブジェクトを呼び出すこともできませ.equals()
ん.hashCode()
。そのHashtable
ため、ハッシュを計算してキーとして使用することはできません。
HashtableとConcurrentHashMapがnullキーまたは値を許可しない主な理由は、それらがマルチスレッド環境で使用されることが予想されるためです。少しの間、null値が許可されていると仮定しましょう。この場合、ハッシュテーブルの「get」メソッドの動作はあいまいです。キーがマップに見つからない場合はnullを返すことができ、キーが見つかり、その値がnullの場合はnullを返すことができます。コードがnull値を予期する場合、通常、キーがマップに存在するかどうかをチェックして、キーが存在しないか、キーは存在するが値がnullであるかを認識できるようにします。これで、このコードはマルチスレッド環境で機能しなくなります。以下のコードを見てみましょう。
if (map.contains(key)) {
return map.get(key);
} else {
throw new KeyNotFoundException;
}
上記のコードで、スレッドt1がcontainsメソッドを呼び出してキーを見つけ、キーが存在し、nullかどうかに関係なく値を返す準備ができていると想定するとします。map.getを呼び出す前に、別のスレッドt2がそのキーをマップから削除します。ここで、t1が再開し、nullを返します。ただし、コードによると、キーが削除されているため、t1の正解はKeyNotFoundExceptionです。しかし、それでもnullを返すため、期待される動作は壊れます。
ここで、通常のHashMapの場合、単一のスレッドによって呼び出されると想定されているため、「contains」チェックと「get」の途中でキーが削除される可能性はありません。したがって、HashMapはnull値を許容できます。ただし、HashtableとConcurrentHashMapの場合、複数のスレッドがデータに作用することが予想されます。したがって、彼らはnull値を許可し、間違った答えを出す余裕はありません。同じロジックがキーにも当てはまります。これで、カウンター引数は次のようになります。2番目のステップが実行される前に別のスレッドがマップ/テーブルを変更できるため、HashtablesおよびConcurrentHashMapsのnull以外の値に対してcontainsおよびgetステップが失敗する可能性があります。それは正しいです、それは起こる可能性があります。ただし、HashtablesとConcurrentHashMapsはnullキーと値を許可しないため、そもそもcontainsを実装してチェックする必要はありません。getメソッドがnullを返す場合、その唯一の理由はキーが存在しないことであり、値がnullである可能性があるためではないことを知っているため、値を直接取得できます。contains and getチェックはHashMapにのみ必要です。これは、HashMapがnull値を許可し、キーが見つからないか値がnullであるかについてのあいまいさを解決する必要があるためです。
理由は、受け入れられた答えの理由です:ハッシュテーブルは古いです。
ただし、すべてのシナリオでHashMapを使用することをお勧めしますが、Hashtableの使用は推奨されていません。
- ハッシュテーブルは同期されているため、THREAD-SAFEです。HashMapはそうではありません。
HashtableもConcurrentHashMapもnullキーまたは値をサポートしていません。HashMapはそうします。
クラスを変更する以外に何も必要とせず、すべてのシナリオで機能するドロップイン置換が必要な場合は、何もありません。最も類似したオプションは、ConcurrentHashMap(スレッドセーフですが、テーブル全体のロックをサポートしていません)です。
このクラスは、スレッドセーフに依存しているが、同期の詳細には依存していないプログラムで、Hashtableと完全に相互運用できます。
HashMapは、シングルスレッドアプリケーションのより良い代替品であるか、同期がパフォーマンスに影響を与えるため、同期は必須ではありません。
出典:
結論として
HashTableでは、要素を配置すると、キーと値のハッシュが考慮されるためです。基本的にあなたは次のようなものを持っているでしょう:
public Object put(Object key, Object value){
key.hashCode();
//some code
value.hashCode();
}
HashTable-nullキーを許可しませんこれは、put(K key、V value)メソッドに、nullポインター例外をスローするkey.hashcode()があるためです。HashTable-null値を許可しないこれは、put(K key、V value)メソッドにif(value == null){thrownewNullPointerExceptionがあるためです。
HashMapは、HashTableのようなチェックがないため、null値を許可しますが、nullキーは1つしか許可しません。これは、キーがnullとして提供されるたびに、内部配列の0番目のインデックスに値を追加するputForNullKeyメソッドを使用して行われます。
デフォルトのハッシュテーブル実装には、nullポインター例外をスローするnullチェックがあります。後でJava開発者は、nullキー(デフォルト値など)と値の重要性と、HashMapが導入された理由に気付いたかもしれません。
HashMapの場合、キーのnullチェックがあり、キーがnullの場合、その要素はハッシュコードが不要な場所に格納されます。
ハッシュテーブルは、JDK1.0以降の非常に古いクラスです。
これを理解するには、まず、このクラスに作成者が書いたコメントを理解する必要があります。「このクラスは、キーを値にマップするハッシュテーブルを実装します。null以外のオブジェクトは、キーまたは値として使用できます。ハッシュテーブルからオブジェクトを正常に保存および取得するには、キーとして使用されるオブジェクトがhashCodeメソッドとequalsメソッドを実装する必要があります。」</ p>
HashTableクラスは、ハッシュメカニズムに実装されています。つまり、キーオブジェクトの必要なハッシュコードであるキーと値のペアを格納することを意味します。キーがnullの場合、ハッシュを指定することはできません。nullポインターの例外と同様のケースを介して、値がnullの場合はnullをスローします。
しかし後で、nullキーと値には独自の重要性があることがわかりました。そのため、HashMapクラスのような後で実装されるクラスでは1つのnullキーと複数のnull値が許可されます。
ハッシュマップの場合、ヌルキーが許可され、キーがヌルの場合はキーのヌルチェックがあり、その要素はエントリ配列のゼロの場所に格納されます。デフォルト値に使用できるnullキー。
=>ハッシュテーブルメソッドは同期され、オブジェクトベースのロックを使用しません。
HashMapは、特別なものと見なして実装します
static final int hash(Object key) {
int h;
return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
}
Java 8では、ハッシュテーブルの型を推測することはできません。
private Map<String,String> hashtable = new Hashtable<>(); // Not Allowed
private Map<String,String> hashtable = new HashMap<>(); // Allowed
@Jainendraが言ったように、HashTableはの呼び出しkey.hashCode()にnullキーを許可しませんput()
。
しかし、null値が許可されない理由を明確に答える人はいないようです。
public synchronized V put(K key, V value) {
// Make sure the value is not null
if (value == null) {
throw new NullPointerException();
}
// Makes sure the key is not already in the hashtable.
Entry<?,?> tab[] = table;
int hash = key.hashCode();
int index = (hash & 0x7FFFFFFF) % tab.length;
@SuppressWarnings("unchecked")
Entry<K,V> entry = (Entry<K,V>)tab[index];
for(; entry != null ; entry = entry.next) {
if ((entry.hash == hash) && entry.key.equals(key)) {
V old = entry.value;
entry.value = value;
return old;
}
}
addEntry(hash, key, value, index);
return null;
}
nullチェック入力は、null値が不正である理由を説明せず、null以外の不変条件を保証するだけです。
null値を許可しないための具体的な答えは、HashTableが呼び出しvalue.equals
時に呼び出すことcontains/remove
です。
Hashtableはnullキーを許可しませんが、HashMapは1つのnullキーと任意の数のnull値を許可します。その背後にある簡単な説明があります。
put()
nullがキーとして渡され、null Keyが特殊なケースとして処理される場合、hashmapのメソッドはhashcode()を呼び出しません。HashMapはnullキーをバケット0に配置し、nullをキーとして渡された値にマップします。
public V put(K key, V value) {
if (key == null)
return putForNullKey(value);
int hash = hash(key);
int i = indexFor(hash, table.length);
for (Entry<K,V> e = table[i]; e != null; e = e.next) {
Object k;
if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
V oldValue = e.value;
e.value = value;
e.recordAccess(this);
return oldValue;
}
}
modCount++;
addEntry(hash, key, value, i);
return null;
}
アルゴリズムに見られるように、put()
メソッドはキーがnullであるかどうかをチェックし、putForNullKey(value)を呼び出して戻ります。このputForNullKeyは、バケットのインデックス0にエントリを作成します。インデックスゼロは、バケット内のnullキー用に常に予約されています。
一方、キーとして使用されるハッシュテーブルオブジェクトの場合は、hashCodeメソッドとequalsメソッドを実装する必要があります。nullはオブジェクトではないため、これらのメソッドを実装することはできません。
Hashtableは、Javaの最初のバージョンに付属しているクラスです。それがリリースされたとき、Javaエンジニアはnullキーの使用を思いとどまらせようとしたか、おそらくその有用性に気づいていませんでした。したがって、彼らはハッシュテーブルでそれを許可しませんでした。
キーと値のペアをハッシュテーブルに挿入するputメソッドは、値がnullの場合にNullPointerExceptionをスローします。Hashtableはハッシュメカニズムに基づいているため、ハッシュはキーに対して計算され、キーがnullの場合はNullPointerExceptionがスローされます。
後でJavaエンジニアは、nullキーと値を持つことには、デフォルトの場合にそれらを使用するような用途があることに気付いたに違いありません。そこで、彼らはHashMapクラスに、nullキーと値を格納する機能を備えたJava5のコレクションフレームワークを提供しました。
キーと値のペアをHashMapに挿入するputメソッドは、nullキーをチェックし、内部テーブル配列の最初の場所に格納します。null値を恐れず、HashtableのようにNullPointerExceptionをスローしません。
異なるキーに複数のnull値を関連付けることができますが、キーは一意である必要があるため、nullキーは1つしか存在できません。
HashTable-nullキーを許可しません
これは、put(K key、V value)メソッドで、key.hashcode()
nullポインター例外をスローするためです。
HashTable-null値を許可しません
これは、put(K key、V value)メソッドにif(value==null){throw new NullPointerException
HashMapは、HashTableのようなチェックがないため、null値を許可しますが、nullキーは1つしか許可しません。これは、キーがnullとして提供されるたびに、内部配列の0番目のインデックスに値を追加するputForNullKeyメソッドを使用して行われます。