開発者は、プログラムの正確さを達成するために、HashMap でのハッシュ衝突の問題を回避する必要はありません。
ここで理解すべき重要な点がいくつかあります。
- 衝突はハッシュの固有の機能であり、そうでなければなりません。可能な値の数 (あなたの場合は文字列ですが、他の型にも適用されます) は、整数の範囲よりもはるかに大きくなります。
- ハッシュのすべての使用法には、衝突を処理する方法があり、Java コレクション (HashMap を含む) も例外ではありません。
- ハッシュは等価テストには含まれません。等しいオブジェクトは等しいハッシュコードを持つ必要があるのは事実ですが、その逆は正しくありません。多くの値が同じハッシュコードを持つことになります。そのため、ハッシュコードの比較を同等性の代用として使用しないでください。コレクションはそうではありません。ハッシュを使用してサブコレクション (Java コレクションの世界ではバケットと呼ばれます) を選択しますが、.equals() を使用して実際に等価性をチェックします。
- コレクションで不正確な結果を引き起こす衝突について心配する必要がないだけでなく、ほとんどのアプリケーションでは、*通常* パフォーマンスについて心配する必要もありません。Java のハッシュされたコレクションは、ハッシュコードを適切に管理します。
- さらに良いことに、(キーとしての文字列) について尋ねた場合、Java の String クラスは非常に優れたハッシュコードを生成するため、ハッシュコード自体について心配する必要さえありません。提供されているほとんどの Java クラスも同様です。
必要に応じて、さらに詳細を以下に示します。
ハッシュが機能する方法(特に、あなたが尋ねたJavaのHashMapのようなハッシュされたコレクションの場合)は次のとおりです。
HashMap は、指定した値をバケットと呼ばれるサブコレクションのコレクションに格納します。これらは、実際には連結リストとして実装されています。これらの数には制限があります: iirc、デフォルトで開始する 16 で、マップにアイテムを追加すると数が増えます。値よりも多くのバケットが常に存在する必要があります。一例を挙げると、デフォルトを使用して、HashMap に 100 エントリを追加すると、256 バケットになります。
マップでキーとして使用できるすべての値は、ハッシュコードと呼ばれる整数値を生成できなければなりません。
HashMap は、このハッシュコードを使用してバケットを選択します。最終的には、これは整数値modulo
をバケットの数にすることを意味しますが、その前に、Java の HashMap には内部メソッド ( と呼ばれるhash()
) があり、ハッシュコードを微調整して、いくつかの既知の凝集の原因を減らします。
値を検索するとき、HashMap はバケットを選択し、.equals()
.
したがって、正確さのために衝突を回避する必要はなく、通常はパフォーマンスについて心配する必要もありません。ネイティブ Java クラス (String など) を使用している場合は、心配する必要はありません。ハッシュコード値を生成します。
独自の hashcode メソッドを作成する必要がある場合 (つまり、名前と姓のペアなど、複合値を持つクラスを作成した場合)、状況は少し複雑になります。ここで間違いを犯す可能性は十分にありますが、ロケット科学ではありません。まず、これを知っておいてください: 正確性を保証するためにしなければならない唯一のことは、等しいオブジェクトが等しいハッシュコードを生成することを保証することです。したがって、クラスの hashcode() メソッドを作成する場合は、equals() メソッドも作成する必要があり、それぞれで同じ値を調べる必要があります。
悪いが正しい hashcode() メソッドを書くことは可能です。これは、「等しいオブジェクトは等しいハッシュコードを生成する必要がある」という制約を満たすことを意味しますが、多くの衝突が発生するため、パフォーマンスは依然として非常に低くなります。
これの標準的な退化の最悪のケースは、すべてのケースに対して単純に定数値 (たとえば 3) を返すメソッドを作成することです。これは、すべての値が同じバケットにハッシュされることを意味します。
それでも機能しますが、パフォーマンスはリンクされたリストのパフォーマンスに低下します。
明らかに、そのようなひどい hashcode() メソッドを作成することはありません。適切な IDE を使用している場合は、IDE を生成することができます。StackOverflow はコードが大好きなので、上記の名/姓クラスのコードを次に示します。
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;
}
}