ほとんどの人はハッシュテーブルデータ構造に精通していると思います。多くの場所で、ハッシュテーブルを使用して検索効率を向上させます。 Javaのオブジェクトクラスにはメソッドがあります。
public Native int hashcode();
この方法の宣言によれば、メソッドはタイプintの数値を返し、ローカルメソッドであるため、オブジェクトクラスで特定の実装は与えられていないことがわかります。
オブジェクトクラスにそのような方法が必要なのはなぜですか?その機能は何ですか?今日は、ハッシュコード法について詳しく説明します。
1。ハッシュコード法の関数
コンテナタイプを含むプログラミング言語の場合、ハッシュコードが基本的に関与しています。 Javaでも同じことが言えます。ハッシュコード法の主な機能は、ハッシュベースのセットで通常作業することです。このようなハッシュセットには、ハッシュセット、ハッシュマップ、ハッシュテーブルが含まれます。
なぜそう言うのですか?オブジェクトがコレクションに挿入されたときに、コレクションにオブジェクトが既に存在するかどうかを判断する方法を考えてみましょう。 (注:複製要素はコレクションでは許可されていません)
おそらく、ほとんどの人は、Equalsメソッドを1つずつ比較するために呼び出すことを考えるでしょう。この方法は確かに実現可能です。ただし、セットにすでに1万個以上のデータまたはそれ以上のデータがある場合、Equalsメソッドが1つずつ比較するために使用される場合、効率は必然的に問題になります。この時点で、ハッシュコード法の関数が反映されます。新しいオブジェクトをコレクションに追加する場合、対応するハッシュコード値を取得するために、オブジェクトのハッシュコードメソッドが最初に呼び出されます。実際、ハッシュマップの特定の実装では、保存されているオブジェクトのハッシュコード値を保存するためにテーブルを使用します。テーブルにハッシュコード値がない場合、比較せずに直接保存できます。ハッシュコード値が存在する場合、その等しい方法が新しい要素と比較するために呼び出されます。同じことが当てはまる場合、他のアドレスは保存されません。したがって、ここでは紛争解決の問題があります。このようにして、等しい方法が実際に呼び出される回数は大幅に減少します。簡単に言えば、Javaのハッシュコードメソッドは、特定のルールに従ってオブジェクト(オブジェクトのストレージアドレス、オブジェクトのフィールドなど)に数値に関連する情報をマップし、この値はハッシュ値と呼ばれます。次のコードは、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); OldValueを返します。 }} modcount ++; Addentry(Hash、Key、Value、I); nullを返します。 } PUTメソッドは、ハッシュマップに新しい要素を追加するために使用されます。 PUTメソッドの特定の実装から、最初にハッシュコードメソッドが呼び出されて要素のハッシュコード値を取得し、テーブルにハッシュコード値が存在するかどうかを確認することができます。存在する場合は、等しい方法を呼び出して、要素が存在するかどうかを再測定します。存在する場合は、値値を更新し、それ以外の場合はハッシュマップに新しい要素を追加します。ここから、ハッシュコード法が存在して等しい方法への呼び出し数を減らし、プログラムの効率を改善することがわかります。
一部の友人は、デフォルトではハッシュコードがオブジェクトのストレージアドレスを返すと誤って考えています。実際、この見解は不完全です。一部のJVMは、実装されたときにオブジェクトのストレージアドレスを直接返すことは事実ですが、これはほとんどの場合はそうではありません。ストレージアドレスがそれに関連している可能性があるとのみ言えます。以下は、Hotspot JVMでハッシュハッシュ値を生成する実装です。
static inline intptr_t get_next_hash(thread * self、oop obj){intptr_t value = 0; if(hashcode == 0){//このフォームは、無防備なグローバルパークミラーRNGを使用しているため、// 2つのスレッドが同じRNGをレースして生成することができます。 // MPシステムでは、グローバルへの多くのRWアクセスがあるため、//メカニズムは多くのコヒーレンシートラフィックを誘導します。 value = os :: random(); } else if(hashcode == 1){//このバリエーションには、STW操作の間に安定した(idempotent)//の特性があります。これは、1-0 //同期スキームの一部で役立ちます。 intptr_t addrbits = intptr_t(obj)>> 3; value = addrbits ^(addrbits >> 5) ^ gvars.stwrandom; } else if(hashcode == 2){value = 1; //感度テストの場合} else if(hashcode == 3){value = ++ gvars.hc sequence; } else if(hashcode == 4){value = intptr_t(obj); } else {//スレッド固有の状態を備えたMarsagliaのXor-Shiftスキーム//これはおそらく全体的な実装の最良の実装です。 unsigned t = self-> _ hashstatex; t ^=(t << 11); self-> _ hashstatex = self-> _ hashstatey; self-> _ hashstatey = self-> _ hashstatez; self-> _ hashstatez = self-> _ hashstatew; unsigned v = self-> _ hashstatew; v =(v ^(v >> 19)) ^(t ^(t >> 8)); self-> _ hashstatew = v; value = v; } value&= markoopdesc :: hash_mask; if(value == 0)value = 0xbad; assert(value!= markoopdesc :: no_hash、 "Invariant"); tevent(hashcode:generate); return値;}この実装は、hotspot/src/share/vm/runtime/synchronizer.cppファイルにあります。
したがって、一部の人々は、2つのオブジェクトがハッシュコード値に基づいて等しいかどうかを直接判断できると言う人もいますか?異なるオブジェクトは同じハッシュコード値を生成する可能性があるため、確かに不可能です。 2つのオブジェクトがハッシュコード値に基づいて等しいかどうかを判断することはできませんが、2つのオブジェクトはハッシュコード値に基づいて等しくないことを直接判断できます。 2つのオブジェクトのハッシュコード値が等しくない場合、それらは2つの異なるオブジェクトでなければなりません。 2つのオブジェクトが本当に等しいかどうかを判断する場合は、Equalsメソッドを使用する必要があります。
つまり、2つのオブジェクトの場合、等しい方法を呼び出すことによって得られた結果がtrueである場合、2つのオブジェクトのハッシュコード値は等しくなければなりません。
Equalsメソッドによって取得された結果がfalseの場合、2つのオブジェクトのハッシュコード値が違いはない場合があります。
2つのオブジェクトのハッシュコード値が等しくない場合、Equalsメソッドによって取得された結果はfalseでなければなりません。
2つのオブジェクトのハッシュコード値が等しい場合、Equalsメソッドによって取得された結果は不明です。
2.エクスポールメソッドとハッシュコードメソッド
場合によっては、クラスを設計する場合、プログラマーは文字列クラスなどの等しいメソッドを書き換える必要がありますが、equalsメソッドを書き換える際には、ハッシュコードメソッドを書き換える必要があることに注意してください。なぜそう言うのですか?
以下の例を見てみましょう。
パッケージcom.cxh.test1; import java.util.hashmap; import java.util.hashset; import java.util.set; class people {private 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));}}ここでは、Equalsメソッドのみを書き換えています。つまり、2人のオブジェクトが同じ名前と年齢を持っている場合、それらは同じ人と見なされます。
このコードの当初の意図は、「1」の結果を出力することですが、実際には「null」を出力します。なぜ?その理由は、Equalsメソッドを書き換えながらハッシュコードメソッドの書き換えを忘れているからです。
同じ名前と年齢を持つ2つのオブジェクトは、論理的に等しいと判断されますが(文字列クラスと同様)、デフォルトでは、ハッシュコードメソッドがオブジェクトのストレージアドレスをマップすることがわかっているはずです。その後、上記のコードの出力結果が「null」であることは驚くことではありません。その理由は非常に単純で、P1と
system.out.println(hashmap.get(new People( "Jack"、12));この文の新しい人( "Jack"、12)は2つのオブジェクトを生成し、ストレージアドレスは異なる必要があります。
public v get(object key){if(key == null)return getfornullkey(); int hash = hash(key.hashcode()); for(entry <k、v> e = table [indexfor(hash、table.length)]; e!= null; e = e.next){object k; if(e.hash == hash &&((k = e.key)== key || key.equals(k)))return e.value; } nullを返します。 }したがって、ハッシュマップを取得するために使用される場合、取得したハッシュドー値は異なるためです(上記のコードは場合によっては同じハッシュコード値を取得する可能性があることに注意してください。
したがって、上記のコードに結果「1」を出力する場合、非常に簡単です。ハッシュコードメソッドを書き換えて、Equalsメソッドとハッシュコードメソッドを常に論理的に一貫性にするだけです。
パッケージcom.cxh.test1; import java.util.hashmap; import java.util.hashset; import java.util.set;クラスPeople {private string name;プライベートインクエイジ;パブリック(文字列名、int age){this.name = name; this.age = age; } public void Setage(int age){this.age = age; } @Override public int hashcode(){// todo auto-enerated 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))); }}このようにして、出力の結果は「1」になります。
次の文章は、Execument Javaの本から抜粋されています。
プログラムの実行中、Equalsメソッドの比較操作で使用される情報が変更されていない限り、ハッシュコード法は一貫して同じ整数を返す必要があります。
2つのオブジェクトがEqualsメソッドに従って等しい場合、2つのオブジェクトのハッシュコードメソッドを呼び出す必要がある場合、同じ整数結果を返す必要があります。
2つのオブジェクトが等しい方法に従って不等式と比較される場合、ハッシュコード法は必ずしも異なる整数を返すとは限りません。
2番目と3番目の記事を理解するのは簡単ですが、最初の記事はしばしば無視されます。また、本「Javaプログラミング思考」のページP495の最初のパッセージに似たパッセージもあります。
「HashCode()を設計する際の最も重要な要素は次のとおりです。同じオブジェクトでHashCode()を呼び出すときは、同じ値を生成する必要があります。Put()でオブジェクトがHashMapに追加された場合、Get()で削除されると別のハッシュコード値が生成されます。 HashCode()メソッドは、異なるハッシュコードを生成します。」
これが例です:
パッケージcom.cxh.test1; Import Java.util.hashmap; import java.util.hashset; import java.util.set; class people {private string name;プライベートインクエイジ;パブリック(文字列名、int age){this.name = name; this.age = age; } public void Setage(int age){this.age = age; } @Override public int hashcode(){// todo auto-enerated 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); p1.setage(13); system.out.println(hashmap.get(p1)); }}このコード出力の結果は「null」であり、誰もがその理由について明確でなければなりません。
したがって、ハッシュコードメソッドと等しいメソッドを設計する場合、オブジェクト内のデータが揮発性である場合、等しいメソッドとハッシュコードメソッドでこのフィールドに依存しないことが最善です。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。