Stringクラスの hashCode() メソッドはnotであり、異なるString-sに対して一意のハッシュコードを生成することが保証されていることを理解しています。 HashMap-sに文字列キーを配置する(デフォルトのString hashCode()メソッドを使用して)多くの使用法を見ています。マップput
が以前に明確に異なる文字列キーでマップに配置されたHashMapエントリを置き換えた場合、この使用法の多くは重大なアプリケーションの問題を引き起こす可能性があります。
String.hashCode()が異なるString-sに対して同じ値を返すシナリオに遭遇する可能性はどのくらいですか?キーが文字列の場合、開発者はこの問題をどのように回避しますか?
開発者は、プログラムの正確性を実現するために、HashMapのハッシュ衝突の問題を回避する必要はありません。
ここで理解すべき重要な点がいくつかあります。
必要に応じて、さらに詳細に説明します。
ハッシュの仕組み(特に、JavaのHashMapのようなハッシュされたコレクションの場合、これはあなたが尋ねたものです)はこれです:
HashMapは、バケットと呼ばれるサブコレクションのコレクションに、指定した値を保存します。これらは実際にはリンクリストとして実装されます。これらの制限された数があります:iirc、デフォルトで開始する16、およびマップに項目を追加するにつれて数が増加します。値よりも常にバケットが多いはずです。 1つの例を提供するために、デフォルトを使用して、HashMapに100個のエントリを追加すると、256個のバケットがあります。
マップでキーとして使用できるすべての値は、ハッシュコードと呼ばれる整数値を生成できる必要があります。
HashMapはこのハッシュコードを使用してバケットを選択します。最終的に、これは整数値modulo
バケット数を取得することを意味しますが、その前に、JavaのHashMapには内部メソッド(hash()
と呼ばれる)があります。凝集。
値を検索するとき、HashMapはバケットを選択し、.equals()
を使用してリンクリストの線形検索により個々の要素を検索します。
そのため、正確さのために衝突を回避する必要はありません。通常、パフォーマンスのために衝突を心配する必要はありません。また、ネイティブのJavaクラス(Stringなど) 、ハッシュコード値の生成について心配する必要もありません。
独自のハッシュコードメソッドを記述する必要がある場合(つまり、名/姓のペアのような複合値を持つクラスを記述したことを意味する)、事態はやや複雑になります。ここで間違っている可能性は十分にありますが、ロケット科学ではありません。最初に、これを知ってください:正確さを保証するためにあなたがしなければならないことは、等しいオブジェクトが等しいハッシュコードを生成することを保証することです。したがって、クラスのhashcode()メソッドを記述する場合、equals()メソッドも記述する必要があり、それぞれの同じ値を調べる必要があります。
悪いが正しいhashcode()メソッドを書くことは可能です。つまり、「等しいオブジェクトは等しいハッシュコードを生成する必要があります」という制約を満たしますが、多くの衝突があるため、パフォーマンスは非常に悪くなります。
これの標準的な縮退最悪の場合は、すべての場合に単純に定数値(3など)を返すメソッドを記述することです。これは、すべての値が同じバケットにハッシュされることを意味します。
それでもworkになりますが、パフォーマンスはリンクリストのパフォーマンスに低下します。
明らかに、このようなひどいhashcode()メソッドを書くことはありません。適切なIDEを使用している場合は、IDEを生成できます。 StackOverflowはコードが大好きなので、上記のfirstname/lastnameクラスのコードを次に示します。
public class SimpleName {
private String firstName;
private String lastName;
public SimpleName(String firstName, String lastName) {
super();
this.firstName = firstName;
this.lastName = lastName;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result
+ ((firstName == null) ? 0 : firstName.hashCode());
result = prime * result
+ ((lastName == null) ? 0 : lastName.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
SimpleName other = (SimpleName) obj;
if (firstName == null) {
if (other.firstName != null)
return false;
} else if (!firstName.equals(other.firstName))
return false;
if (lastName == null) {
if (other.lastName != null)
return false;
} else if (!lastName.equals(other.lastName))
return false;
return true;
}
}
HashMap.put
メソッドは、 String.hashCode
を見ただけではキーが同じかどうかを判断しないと強く思います。
ハッシュ衝突 の可能性は間違いなくあるので、 String.equals
メソッドも呼び出されて- String
sは、2つのString
sがhashCode
から返された同じ値を持つ場合が実際にある場合、本当に等しいです。
したがって、新しいキーString
は、 String
によって返される値が等しく、かつ HashMap
である場合にのみ、hashCode
に既に存在するものと同じキーequals
と判断されます。 メソッドはtrue
を返します。
また、追加するには、この考えはString
以外のクラスにも当てはまります。これは、 Object
クラス自体に hashCode
および equals
メソッドが既にあるためです。
編集
したがって、質問に答えるには、いいえ、String
のキーにHashMap
を使用することは悪い考えではありません。
これは問題ではなく、ハッシュテーブルの仕組みです。整数よりもはるかに明確な文字列があるため、すべての明確な文字列に明確なハッシュコードを設定することは不可能です。
他の人が書いたように、ハッシュの衝突はequals()メソッドを介して解決されます。これが引き起こす唯一の問題は、ハッシュテーブルの縮退であり、パフォーマンスの低下につながります。 JavaのHashMapが 負荷係数 を持っているのはこのためです。これは、バケットと挿入された要素の比率であり、超過すると、バケット数が2倍のテーブルの再ハッシュを引き起こします。
これは通常非常にうまく機能しますが、ハッシュ関数が良好な場合、つまり特定の入力セットに対して統計的に予想される衝突数を超えない場合にのみ有効です。 String.hashCode()
はこの点で優れていますが、常にそうであるとは限りませんでした。 申し立て 、Java 1.2より前)は、n番目の文字ごとに含まれています。これは高速でしたが、n番目の文字を共有するすべての文字列で予測可能な衝突を引き起こしました。あなたがそのような通常の入力をするのに十分に不運であるか、誰かがあなたのアプリにDOS攻撃をしたいならば、悪いです。
あなたはハッシュ衝突について話している。ハッシュの衝突は、hashCodeされるタイプに関係なく問題です。 hashCode(HashMapなど)を使用するすべてのクラスは、ハッシュ衝突をうまく処理します。たとえば、HashMapはバケットごとに複数のオブジェクトを保存できます。
HashCodeを自分で呼び出す場合を除き、心配する必要はありません。ハッシュの衝突はまれですが、何も壊しません。