HashMaphashCode()、==およびequals()エントリの検索に使用します。特定のキーのルックアップ シーケンスkは次のとおりです。
- エントリが格納されているバケットを特定するために使用
k.hashCode()します (存在する場合)。
k1見つかった場合、そのバケット内の各エントリのキーについて、 の場合はのエントリk == k1 || k.equals(k1)を返しますk1
- その他の結果、対応するエントリなし
例を使用して説明するために、クラスHashMapで表される同じ整数値を持つ場合、キーが「論理的に同等」である場所を作成するとします。AmbiguousInteger次に、 を作成しHashMap、1 つのエントリに入れ、その値をオーバーライドして、キーで値を取得しようとします。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
}
HashMap<AmbiguousInteger, Integer> map = new HashMap<>();
// logically equivalent keys
AmbiguousInteger key1 = new AmbiguousInteger(1),
key2 = new AmbiguousInteger(1),
key3 = new AmbiguousInteger(1);
map.put(key1, 1); // put in value for entry '1'
map.put(key2, 2); // attempt to override value for entry '1'
System.out.println(map.get(key1));
System.out.println(map.get(key2));
System.out.println(map.get(key3));
Expected: 2, 2, 2
hashCode()and をオーバーライドしないでくださいequals()。デフォルトでは、Java はhashCode()オブジェクトごとに異なる値を生成するため、HashMapこれらの値を使用して異なるバケットにマッピングkey1します。対応するバケットがないため、値がありません。key2key3
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 2, set as entry 2[1]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 2, get as entry 2[1]
map.get(key3); // map to no bucket
Expected: 2, 2, 2
Output: 1, 2, null
上書きhashCode()のみ: とを同じバケットにHashMapマップしますが、デフォルトではチェックを使用し、異なるインスタンスを参照するため、とチェックの両方が失敗するため、それらは異なるエントリのままです。は両方とも失敗し、andに対してチェックするため、対応する値はありません。key1key2key1 == key2key1.equals(key2)equals()==key3==equals()key1key2
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[2]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[2]
map.get(key3); // map to bucket 1, no corresponding entry
Expected: 2, 2, 2
Output: 1, 2, null
オーバーライドequals()のみ: HashMapデフォルトが異なるため、すべてのキーを異なるバケットにマップしますhashCode()。==またはequals()チェックはHashMap、それらを使用する必要があるポイントに決して到達しないため、ここでは無関係です。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 2, set as entry 2[1]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 2, get as entry 2[1]
map.get(key3); // map to no bucket
Expected: 2, 2, 2
Actual: 1, 2, null
hashCode()とequals(): HashMapmapskey1の両方key2をオーバーライドkey3し、同じバケットに入れます。==異なるインスタンスを比較するとチェックは失敗しますequals()が、それらはすべて同じ値を持ち、ロジックによって「論理的に同等」と見なされるため、チェックはパスします。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return value;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[1], override value
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[1]
map.get(key3); // map to bucket 1, get as entry 1[1]
Expected: 2, 2, 2
Actual: 2, 2, 2
hashCode()ランダムだったら?:HashMap操作ごとに異なるバケットが割り当てられるため、以前に入力したものと同じエントリが見つかることはありません。
class AmbiguousInteger {
private static int staticInt;
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return ++staticInt; // every subsequent call gets different value
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 2, set as entry 2[1]
map.get(key1); // map to no bucket, no corresponding value
map.get(key2); // map to no bucket, no corresponding value
map.get(key3); // map to no bucket, no corresponding value
Expected: 2, 2, 2
Actual: null, null, null
hashCode()いつも同じだったら?:HashMapすべてのキーを 1 つの大きなバケットにマップします。この場合、コードは機能的には正しいですが、 の使用HashMapは実質的に冗長です。これは、取得が O(N) 時間 ( Java 8 の場合は O(logN) )でその単一のバケット内のすべてのエントリを反復処理する必要があるためです。の使用にList。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return obj instanceof AmbiguousInteger && value == ((AmbiguousInteger) obj).value;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[1]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[1]
map.get(key3); // map to bucket 1, get as entry 1[1]
Expected: 2, 2, 2
Actual: 2, 2, 2
equalsが常に false の場合はどうなるでしょうか。:==同じインスタンスをそれ自体と比較するとチェックに合格しますが、それ以外の場合は失敗します。equalsチェックは常に失敗しkey1、 「論理的に異なる」key2とkey3見なされ、異なるエントリにマップされますが、同じために同じバケット内にありますhashCode()。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return false;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[2]
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[2]
map.get(key3); // map to bucket 1, no corresponding entry
Expected: 2, 2, 2
Actual: 1, 2, null
equalsが常に true の場合はどうなりますか? : 基本的に、すべてのオブジェクトが別のオブジェクトと「論理的に同等」であると見なされると言っているため、それらはすべて同じバケット (同じためhashCode())、同じエントリにマップされます。
class AmbiguousInteger {
private final int value;
AmbiguousInteger(int value) {
this.value = value;
}
@Override
public int hashCode() {
return 0;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
map.put(key1, 1); // map to bucket 1, set as entry 1[1]
map.put(key2, 2); // map to bucket 1, set as entry 1[1], override value
map.put(new AmbiguousInteger(100), 100); // map to bucket 1, set as entry1[1], override value
map.get(key1); // map to bucket 1, get as entry 1[1]
map.get(key2); // map to bucket 1, get as entry 1[1]
map.get(key3); // map to bucket 1, get as entry 1[1]
Expected: 2, 2, 2
Actual: 100, 100, 100