web-dev-qa-db-ja.com

Hashtableがnullキーまたはnull値を許可しないのはなぜですか?

JDKドキュメントで指定されているように、Hashtableはnullキーまたは値を許可しません。 HashMapでは、1つのヌルキーと任意の数のヌル値を使用できます。どうしてこれなの?

33
Love Hasija

Hashtableは古いクラスであり、その使用は一般的に推奨されていません。おそらく彼らは、nullキー、さらに重要なのはnull値の必要性を認識し、HashMap実装に追加しました。

HashMapはより新しく、より高度な機能を備えています。これは基本的にHashtable機能の改善にすぎません。 HashMapの作成時には、null値をキーとして処理し、特殊なケースとして処理するように特別に設計されていました。

編集

Hashtableから JavaDoc

Hashtableからオブジェクトを正常に保存および取得するには、キーとして使用されるオブジェクトにhashCodeメソッドとequalsメソッドを実装する必要があります。

nullはオブジェクトではないため、.equals()または.hashCode()を呼び出すことはできません。したがって、Hashtableはハッシュを計算できません。キーとして使用します。

41
Jainendra

HashtableおよびConcurrentHashMapがnullキーまたはnull値を許可しない主な理由は、マルチスレッド環境で使用されることが予想されるためです。しばらくの間、null値が許可されていると仮定しましょう。この場合、getメソッドの動作はあいまいです。キーがマップ内で見つからない場合はnullを返すことができ、キーが見つかりその値がnullである場合はnullを返すことができます。コードがnull値を予期する場合、通常、キーが存在しないか、キーは存在するがvalueは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の場合、複数のスレッドがデータに作用することが予想されます。したがって、ヌル値を許可して誤った答えを出す余裕はありません。キーについても同じロジックが適用されます。カウンター引数は次のようになります-2番目のステップが実行される前に別のスレッドがマップ/テーブルを変更できるため、HashtablesおよびConcurrentHashMapsのnull以外の値に対して、containsおよびgetステップが失敗する場合があります。それは正しいです、それは起こる可能性があります。ただし、HashtablesとConcurrentHashMapsはnullキーと値を許可しないため、最初に包含を実装してチェックを取得する必要はありません。 getメソッドがnullを返す場合、キーが存在しないことが唯一の理由であり、値がnullになる可能性があるためではないことを知っているため、値を直接取得できます。 HashMapはnull値を許可し、キーが見つからないか値がnullであるかどうかのあいまいさを解決する必要があるため、containsおよびgetチェックはHashMapにのみ必要です。

5
punitoza

その理由は、受け入れられた答えの理由です:ハッシュテーブルは古いです。

ただし、Hashtable ISを使用することは、あらゆるシナリオでHashMapを支持して推奨されません。

  • ハッシュテーブルは同期されているため、 THREAD-SAFE です。 HashMapはそうではありません。

HashtableもConcurrentHashMapもnullキーまたは値をサポートしていません。 HashMapはそうです。

クラスを変更する以外に何も必要とせず、すべてのシナリオで機能するドロップイン置換が必要な場合はありません。最も類似したオプションは ConcurrentHashMap (これはスレッドセーフですが、テーブル全体のロックをサポートしていません):

このクラスは、スレッドの安全性に依存しているが同期の詳細に依存していないプログラムでは、Hashtableと完全に相互運用できます。

HashMapは、同期が導入するパフォーマンスへの影響のため、シングルスレッドアプリケーションのより良い代替手段であり、同期は必要ありません。

ソース:

2
NotGaeL

デフォルトのHashtable実装には、nullポインター例外をスローするnullチェックがあります。後でJava開発者は、nullキー(デフォルト値など)と値の重要性と、HashMapが導入された理由に気付いたかもしれません。

HashMapの場合、キーがnullの場合、キーのnullチェックがあり、その要素はハッシュコードが不要な場所に保存されます。

1
psl

ハッシュテーブルはJDK 1.0からの非常に古いクラスです

これを理解するには、まず、このクラスに作成者が書いたコメントを理解する必要があります。 「このクラスは、キーを値にマッピングするハッシュテーブルを実装します。 null以外のオブジェクトは、キーまたは値として使用できます。ハッシュテーブルからオブジェクトを正常に保存および取得するには、キーとして使用するオブジェクトにhashCodeメソッドとequalsメソッドを実装する必要があります。

HashTableクラスはハッシュメカニズムに実装されます。これは、キーと値のペア、キーオブジェクトの必要なハッシュコードを格納することを意味しますキーがnullの場合、ハッシュを指定できず、nullポインター例外および値の類似のケースを介して、値がnullの場合はnullがスローされます。

しかし後で、nullキーと値には独自の重要性があることがわかったため、HashMapクラスのような後の実装クラスで1つのnullキーと複数のnull値が許可されます。

ハッシュマップの場合、nullキーが許可され、キーがnullの場合はキーのnullチェックがあり、その要素はエントリ配列のゼロの場所に格納されます。デフォルト値に使用できるnullキー。

=> Hashtableメソッドは同期され、オブジェクトベースのロックを使用しません。

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
0
Kumar Abhishek

@Jainendraが言ったように、HashTableはput()のkey.hashCode()の呼び出しにnullキーを許可しません。

しかし、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;
}

Putのnullチェックは、null値が不正である理由を説明しません。nullでない不変式を保証するだけです。

Null値を許可しないための具体的な答えは、HashTableがvalue.equals呼び出し時contains/remove

0
Jiacai Liu

結論として

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キー、V値)メソッドにif(value == null){throw new NullPointerExceptionがあるためです

HashMapは、HashTableのようなチェックを持たないため、null値を許可しますが、nullキーは1つしか許可しません。これは、キーがnullとして提供されるたびに、内部配列の0番目のインデックスに値を追加するputForNullKeyメソッドの助けを借りて行われます

0
Rehan