hashcode() メソッドを学習するには、次の点を段階的に理解する必要があります:
1. hashcode() の起源
ハッシュコードは、アドレス、文字列、またはオブジェクトの数値のInt型の値はどのように計算されますか?答えは、ハッシュテーブルです。ハッシュテーブルは、キー値に基づいて直接アクセスされるデータ構造です。これは公式すぎるかもしれませんが、ハッシュコードは関数を介してマッピングされた値であり、関数にキー値を渡すと値が取得されます。ハッシュ関数で得られる値がハッシュ値、つまりvalue=f(key)となります。したがって、ハッシュコードを理解すると、 hashcode() メソッドが何に使用されるかがわかります。 hashcode() メソッドは、ハッシュコード メソッドを計算して返すものです。
2. hashcode() の役割
ハッシュ テーブルの主な利点は、検索効率を向上できることです。 API を見ると、hashcode() は Object のメソッドであることがわかります。Java では、その改良の特性に応じて、通常の状況ではすべてのクラスのオブジェクトが hashcode() メソッドを呼び出すことができます。ハッシュ ベースのコレクションを適切に操作するために、このようなハッシュ コレクションには HashSet、HashMap、HashTable が含まれます。セット内に重複した要素が存在することは許可されていないため、新しい要素を追加する必要がある場合、その要素がすでに存在しているかどうかをどのように判断すればよいでしょうか?おそらくほとんどの人は、equals メソッドを呼び出して 1 つずつ比較することを考えるでしょう。この方法は実際に実行可能です。しかし、コレクション内にすでに 10,000 個以上のデータがあり、それらを 1 つずつ比較するために平等メソッドを使用する場合、効率が問題になります。このとき、hashCode メソッドの役割が反映されます。新しいオブジェクトがコレクションに追加されると、このオブジェクトの hashCode メソッドが最初に呼び出され、対応するハッシュコード値が取得されます。オブジェクトのハッシュコード値がテーブルに存在しない場合、ハッシュコード値が存在する場合は、そのequalsメソッドが呼び出され、その値と比較されます。 new 要素が同じである場合は比較されません。保存された場合、他のアドレスが同じでない場合はハッシュされるため、競合解決の問題が発生するため、実際の等しい呼び出しの数は異なります。方法が大幅に軽減されます。次のコードは、java.util.HashMap の put メソッドの具体的な実装です。
public V put(K key, V value) { if (key == null) return putForNullKey(value); int hash = hash(key.hashCode()); 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 メソッドの具体的な実装から、hashCode が新しい要素を追加するために使用されていることがわかります。まず hashCode 値を取得するためにメソッドが呼び出され、次に hashCode 値がテーブルに存在するかどうかを確認し、要素が存在する場合は、その値を更新します。 、それ以外の場合は、新しい要素を HashMap に追加します。ここから、 hashCode メソッドは、equals メソッドの呼び出し数を減らし、それによってプログラムの効率を向上させるために存在していることがわかります。
3.hashcode()メソッドとequals()メソッド
ここで注意すべき問題がいくつかあります。オブジェクトが等しいかどうかを判断するには、 hashcode() メソッドを使用できます。答えはノーです。equals() メソッドを使用する必要があります。 2 つの異なるオブジェクトは同じハッシュコードを持つことができますが、異なるハッシュコードを持つ 2 つのオブジェクトは異なっている必要があります。
注意すべきもう 1 つの問題: クラスを設計するときは、String クラスなどの等しいメソッドをオーバーライドする必要がありますが、等しいメソッドを書き換えるときは、hashCode メソッドもオーバーライドする必要があることに注意してください。 equals メソッドのみをオーバーライドする例を見てみましょう:
import java.util.HashMap; import java.util.HashSet; import java.util.Set; class People{ private String name; private int age; public People(String name,int age) { this.name = name; this.age = age; } public void setAge(int age){ this.age = age; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return this.name.equals(((People)obj).name) && this.age== ((People)obj).age; } } public class Main { public static void main(String[] args) { People p1 = new People("Jack", 12); System.out.println(p1.hashCode()); HashMap<People, Integer> hashMap = new HashMap<People, Integer>(); hashMap.put(p1, 1); System.out.println(hashMap.get(new People("Jack", 12))); } }
2 つの People オブジェクトの名前と年齢が同じである場合、それらは同一人物とみなされます。このコードの本来の意図は、このコードの出力結果が「1」であることですが、実際には「null」が出力されます。
equals メソッドをオーバーライドすることで、名前と年齢が同じ 2 つのオブジェクトは論理的に等しいオブジェクトであると判断されますが (String クラスと同様)、デフォルトでは hashCode メソッドがオブジェクトのストレージ アドレスをマップすることを知っておく必要があります。上記のコードの出力が「null」であることは驚くべきことではありません。理由は非常に簡単です。この文の p1 と System.out.println(hashMap.get(new People("Jack", 12)); が指すオブジェクトは 2 つのオブジェクトを生成します。 . 、それらのストレージアドレスは異なる必要があります。したがって、ハッシュマップがgetオペレーションを実行すると、取得されたハッシュドー値が異なるため、直接nullが返されます。上記のコードの出力結果を「1」にしたい場合は、equals メソッドと hashCode メソッドが常に論理的に一致するように hashCode メソッドを書き換えるだけで済みます。
import java.util.HashMap; import java.util.HashSet; import java.util.Set; class People{ private String name; private int age; public People(String name,int age) { this.name = name; this.age = age; } public void setAge(int age){ this.age = age; } @Override public int hashCode() { // TODO Auto-generated method stub return name.hashCode()*37+age; } @Override public boolean equals(Object obj) { // TODO Auto-generated method stub return this.name.equals(((People)obj).name) && this.age== ((People)obj).age; } } public class Main { public static void main(String[] args) { People p1 = new People("Jack", 12); System.out.println(p1.hashCode()); HashMap<People, Integer> hashMap = new HashMap<People, Integer>(); hashMap.put(p1, 1); System.out.println(hashMap.get(new People("Jack", 12))); } }
以下は書籍『Effective Java』からの引用です:
プログラムの実行中、equals メソッドの比較演算で使用される情報が変更されない限り、同じオブジェクトが複数回呼び出され、ハッシュコードがメソッドは一貫して同じ整数値を返す必要があります。
equals メソッドの比較に従って 2 つのオブジェクトが等しい場合、2 つのオブジェクトの hashCode メソッドを呼び出すと、同じ整数の結果が返されなければなりません。
equals メソッドに従って 2 つのオブジェクトが等しくない場合、hashCode メソッドは必ずしも異なる整数を返す必要はありません。
2つ目と3つ目はわかりやすいのですが、1つ目は無視されがちです。 『Java Programming Thoughts』という本の P495 ページに、最初の段落と同様の段落があります。「hashCode() を設計する際の最も重要な要素は次のとおりです。hashCode() が同じオブジェクトで呼び出されるときは常に、同じオブジェクトを生成する必要があります」 put() を使用してオブジェクトが HashMap に追加され、get() を使用してオブジェクトが取り出されるときに別の hashCode 値が生成される場合、hashCode メソッドはオブジェクト内の揮発性データとユーザーに依存します。このデータが変更されると、 hashCode() メソッドが別のハッシュ コードを生成するため、注意が必要です。」