JavaのEqualsメソッドとハッシュコードメソッドはオブジェクトにあるため、各オブジェクトにはこれら2つのメソッドがあります。特定のニーズを実装する必要がある場合があり、これら2つの方法を書き直す必要がある場合があります。今日は、これら2つの方法の機能を紹介します。
equals()およびhashcode()メソッドは、同じクラスで同じクラスオブジェクトをコンテナに保存する場合、同じクラスに設定するなどのコンテナに同じクラスオブジェクトを保存する場合、同じクラスで比較するために使用されます。
ここでは、最初に問題を理解する必要があります。
equals()equalsを持つ2つのオブジェクト、HashCode()は等しく、Equals()を持つ2つのオブジェクトは等しくなく、HashCode()が等しくないことを証明できません。言い換えれば、equals()メソッドが等しくない2つのオブジェクトの場合、HashCode()は等しい場合があります。 (私の理解は、生成されたときのハッシュコードの競合によって引き起こされます)
ここで、ハッシュコードは辞書の各文字のインデックスに似ており、equals()は、辞書の同じ文字の下の異なる単語を比較するようなものです。辞書のように、辞書の「自己」という単語の下で「自己」と「自発的」を検索すると、equals()を使用して単語クエリの等式を決定する場合、それは同じ単語です。たとえば、equals()で比較された2つの単語は「self」であり、Hashcode()メソッドによって取得された値は現時点で等しくなければなりません。 equals()メソッドが「自己」と「自発的」という単語を比較する場合、結果はあなたが待ちたくないが、これらの単語は両方とも「自己」という言葉に属し、インデックスを検索するとき、つまり、ハッシュコード()は同じです。 equals()が「自己」と「彼ら」という単語を比較する場合、結果も異なり、HashCode()によって得られた結果も現時点で異なります。
逆に:hashcode()は異なり、equals()を導入できます。 HashCode()は等しく、等しい()は等しい場合があるか、等しくない場合があります。オブジェクトクラスでは、HashCode()メソッドはローカルメソッドであり、オブジェクトのアドレス値を返します。オブジェクトクラスのequals()メソッドは、2つのオブジェクトのアドレス値も比較します。 equals()が等しい場合、2つのオブジェクトのアドレス値も等しく、もちろんHashCode()は等しいことを意味します。
同時に、ハッシュアルゴリズムは要素を見つけるための高い効率を提供します
コレクションにオブジェクトが含まれているかどうかを知りたい場合、おおよそのプログラムコードをどのように記述しますか?
通常、探しているオブジェクトと比較するために、各要素を1つずつ取り出します。要素と探しているオブジェクトとの等しいメソッド比較の結果が見つかった場合は、検索を停止し、肯定的な情報を返します。それ以外の場合は、否定的な情報を返します。コレクションには、10,000個の要素などの多くの要素があり、探しているオブジェクトが含まれていない場合、プログラムはコレクションから10,000個の要素を取り出し、結論を出すために1つずつ比較する必要があることを意味します。
誰かがハッシュアルゴリズムを発明し、セットから要素を見つける効率を改善しました。このようにして、セットはいくつかのストレージエリアに分割されます。各オブジェクトはハッシュコードを計算でき、ハッシュコードをグループ化できます(異なるハッシュ関数を使用して計算)。各グループは、特定のストレージエリアに対応しています。オブジェクトのハッシュによると、オブジェクトを保存する領域を決定できます。ハッシュセットは、ハッシュアルゴリズムを使用してオブジェクトのセットにアクセスします。ハッシュコードをグループ化して分割するために、特定の数値nの残りの部分(このハッシュ関数が最も簡単です)を取得する方法を内部的に使用します。オブジェクトクラスは、各Javaオブジェクトのハッシュコードを返すためのHashCode()メソッドを定義します。 Hashsetコレクションからオブジェクトを探しているとき、Javaシステムは最初にオブジェクトのHashCode()メソッドを呼び出して、オブジェクトのハッシュコードテーブルを取得します。 、次に、ハッシュに基づいて対応するストレージ領域を見つけ、最後にストレージ領域の各要素を取得し、それをオブジェクトと等しい方法と比較します。このようにして、コレクション内のすべての要素を横断せずに結論を得ることができます。ハッシュセットコレクションは優れたオブジェクト検索パフォーマンスを持っていることがわかりますが、ハッシュセットコレクションにオブジェクトを保存する効率は比較的低いことがわかります。これは、ハッシュセットコレクションにオブジェクトを追加する場合、最初にオブジェクトのハッシュコードを計算し、このハッシュコードに基づいてコレクションのオブジェクトのストレージ位置を決定する必要があるためです。クラスのインスタンスオブジェクトをハッシュセットに正常に保存できるようにするために、このクラスの2つのインスタンスオブジェクトの結果は、equals()メソッドと比較した結果が等しい場合にも等しくなければなりません。つまり、obj1.equals(obj2)の結果が当てはまる場合、次の式の結果も真でなければなりません。
obj1.hashcode()== obj2.hashcode()
言い換えれば、オブジェクトの等しい方法を書き直す場合、ハッシュコードメソッドを書き換える必要があります。ただし、ハッシュコードメソッドを書き換えない場合、オブジェクトオブジェクトのハッシュコードメソッドは常にオブジェクトのハッシュアドレスを返し、このアドレスは決して等しくありません。したがって、この時点でequalsメソッドが書き換えられたとしても、ハッシュコードメソッドが待機したくない場合、比較のために等しい方法を呼び出さないため、無意味です。
クラスのハッシュコード()メソッドが上記の要件に従わない場合、このクラスの2つのインスタンスオブジェクト間の比較の結果がequals()メソッドと等しい場合、セットに同時に保存しないでください。ただし、ハッシュコード()メソッドの戻り値が異なるため、ハッシュセットセットに保存されている場合(オブジェクトのハッシュコードメソッドの返信値は常に異なります)、2番目のオブジェクトは最初にハッシュコード計算に従って別の領域に配置できます。オブジェクトクラスのHashCode()メソッドは、Hashsetに保存されているオブジェクトの要件を満たすことができません。その返品値はオブジェクトのメモリアドレスから計算されるためです。プログラムの実行中はいつでも同じオブジェクトによって返されるハッシュ値は常に変更されません。したがって、2つの異なるインスタンスオブジェクトである限り、等しい方法の比較結果が等しい場合でも、デフォルトのハッシュコードメソッドの戻り値は異なります。
特定の例を見てみましょう。
rectObjectオブジェクト:パッケージcom.weijia.demo; public class rectobject {public int x;公開y; public rectobject(int x、int y){this.x = x; this.y = y; } @override public int hashcode(){final int prime = 31; int result = 1; result = prime * result + x; result = prime * result + y;返品結果; } @Override public boolean equals(object obj){if(this == obj)return true; if(obj == null)falseを返します。 if(getClass()!= obj.getClass())falseを返します。 final rectobject other =(rectobject)obj; if(x!= other.x){return false; } if(y!= other.y){return false; } trueを返します。 }}ハッシュコードをオーバーライドし、親クラスオブジェクトのメソッドに等しくなり、ハッシュコードとメソッドに等しい方法を確認します。2つのrectobjectオブジェクトのxとyの値が等しい場合、ハッシュコード値は等しく、等しいリターンは真です。
これがテストコードです:
パッケージcom.weijia.demo; java.util.hashsetをインポートします。 public class demo {public static void main(string [] args){hashset <rectobject> set = new Hashset <RectObject>(); rectObject R1 = new RectObject(3,3); rectObject R2 = new RectObject(5,5); rectObject R3 = new RectObject(3,3); set.add(r1); set.add(r2); set.add(r3); set.add(r1); system.out.println( "size:"+set.size()); }} 4つのオブジェクトをハッシュセットに保存し、セットコレクションのサイズを印刷しました。結果は何ですか?
実行結果:サイズ:2
なぜ2つないのですか? RectObjectクラスのハッシュコードメソッドを書き直すため、これは非常に簡単です。 rectObjectオブジェクトのxおよびy属性値が等しい限り、ハッシュコード値も等しくなります。したがって、最初にハッシュコード値を比較します。 R1およびR2オブジェクトのxおよびy属性値は等しくないため、ハッシュコードは異なるため、R2オブジェクトを入力できますが、R3オブジェクトのxおよびy属性値はR1オブジェクトの属性値と同じであるため、ハッシュコードは等しくなります。この時点では、2つのxとyの値が等しいため、R1とR3の等しい方法を比較しているため、R1とR3オブジェクトは等しいため、R3を入力できません。また、r1を追加することは追加されていないため、設定されたセットには2つのオブジェクト、R1とR2のみがR1とR2のみがあります。
次に、rectObjectオブジェクトのハッシュコードメソッドにコメントします。つまり、オブジェクトオブジェクトのハッシュコードメソッドをオーバーライドして、コードを実行しません。
実行結果:サイズ:3
この結果も非常に簡単です。まず、R1オブジェクトとR2オブジェクトのハッシュコードを判断します。オブジェクトのハッシュコードメソッドは、オブジェクトのローカルメモリアドレスの変換結果を返すため、異なるインスタンスオブジェクトのハッシュコードは異なります。同様に、R3とR1のハッシュコードも不均等ですが、R1 == R1なので、最終セットには3つのオブジェクトR1、R2、およびR3のみがありますので、サイズは3です。
次に、rectObjectオブジェクトのequalsメソッドの内容にコメントし、ハッシュコードメソッドにコメントすることなく直接falseを返し、コードを実行します。
実行結果:サイズ:3
この結果は少し予想外です。分析しましょう。
まず、R1とR2のオブジェクトはハッシュコードを等しく比較しているため、R2を設定し、R3のハッシュコードメソッドを調べ、等しいR1とR3を比較し、等しい方法を比較します。 Equalsメソッドは常にFalseを返すため、R1とR3も不平等であり、R3とR2に言及する必要はありません。 2つのハッシュコードは等しくないため、R3を設定してからR4を見て、R1とR4を比較して、ハッシュコードが等しいことがわかります。 Equalsメソッドを比較すると、equalsはFalseを返すため、R1とR4は等しくなく、同じR2とR4も不均等であり、R3とR4も不平等であるため、R4はセットセットに配置できます。
この時点で、ハッシュセットのソースコードを確認する必要があります。 HashsetのADDメソッドのソースコードは次のとおりです。
/***このセットがまだ存在していない場合、指定された要素をこのセットに追加します。 *より正式には、指定された要素<tt> e </tt>をこのセットに追加します。 *このセットに既に要素が含まれている場合、呼び出しはセットを残します * false </tt>を返します。 * *このセットに追加される@param e要素 * @return <tt> true </ tt>このセットに指定された *要素がまだ含まれていない場合 */ public boolean add(e){return map.put(e、seprent)== null; }ここでは、ハッシュセットが実際にハッシュマップに基づいて実装されていることがわかります。 HashmapのPut Methodをクリックしています。ソースコードは次のとおりです。
/***指定された値を、このマップの指定されたキーと関連付けます。 *マップにキーのマッピングが以前に含まれていた場合、古い *値が置き換えられます。 * *指定された値が関連付けられることである@paramキーは、指定されたキーに関連付けられている * @param値<tt> key </tt>、または * <tt> null </tt>に関連付けられた以前の値を<tt> key </tt>のマッピングがなかった場合。 *(a <tt> null </tt>リターンは、以前に関連付けられている<tt> null </tt>と<tt> key </tt>。) */public v put(k key、v value){if(key == null)return putfornullkey(value); int hash = hash(key); 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を返します。 }主にifの判断条件を見てみましょう。
まず、ハッシュコードが等しいかどうかを判断します。等しくない場合は、直接スキップします。等しい場合は、これらの2つのオブジェクトが等しいか、これら2つのオブジェクトの等しい方法であるかを比較します。実行または操作されているため、1つが真である限り、ここで説明できます。実際、上記のセットのサイズは3です。最後のR1が入力されておらず、R1 == R1が真であると考えられていたため、入力されていないためです。したがって、セットのサイズは3です。ハッシュコードメソッドを常に返すように設定すると、このセットは4になります。
最後に、ハッシュコードによるメモリリークを見てみましょう。コードをご覧ください。
パッケージcom.weijia.demo; java.util.hashsetをインポートします。 public class demo {public static void main(string [] args){hashset <rectobject> set = new Hashset <RectObject>(); rectObject R1 = new RectObject(3,3); rectObject R2 = new RectObject(5,5); rectObject R3 = new RectObject(3,3); set.add(r1); set.add(r2); set.add(r3); r3.y = 7; system.out.println( "削除前のサイズ:"+set.size()); set.remove(r3); system.out.println( "削除後のサイズ:"+set.size()); }}実行結果:
削除前のサイズ:3
削除されたサイズ:3
ラッシュ、私は問題を見つけました、そしてそれは大きな問題でした。 R3オブジェクトを削除するために削除を呼び出して、削除されたと考えていますが、実際には削除されていません。これは、未使用のオブジェクトであるメモリリークと呼ばれますが、メモリにあります。したがって、これを何度も操作した後、記憶は爆発します。削除のソースコードをご覧ください。
/***存在する場合は、このセットから指定された要素を削除します。 *より正式には、 * <tt> e </tt>を削除して、 * <tt>(o == null?e == null:o.equals(e))</tt>、 *このセットにそのような要素が含まれている場合。 <tt> true </tt>を返します *このセットに要素が含まれている(または同等に、このセット *が通話の結果として変更された場合)。 (このセットには、呼び出しが返されると *要素が含まれません。) * * @param oオブジェクトは、このセットから削除されます。 }
次に、削除メソッドのソースコードを見てください。
/***存在する場合、このマップから指定されたキーのマッピングを削除します。 * *マッピングがマップから削除される@paramキー * @@return <tt> key </tt>または * <tt> null </tt>に関連付けられた前の値は、<tt> key </tt>のマッピングがない場合。 *(a <tt> null </tt> returnは、以前に関連付けられている<tt> null </tt>と<tt> key </tt>。) */public v remove(object key){entry <k、v> e = removeentryforkey(key); return(e == null?null:e.value); } RemoveEntryForKeyメソッドソースコードを見てみましょう。
/** *ハッシュマップの指定されたキー *に関連付けられたエントリを削除および返します。このキーのハッシュマップにマッピング *が含まれていない場合、nullを返します。 */ final entry <k、v> removeEntryforkey(object key){int hash =(key == null)? 0:ハッシュ(キー); int i = indexfor(hash、table.length);エントリ<k、v> prev = table [i];エントリ<k、v> e = prev; while(e!= null){entry <k、v> next = e.next;オブジェクトk; if(e.hash == hash &&((k = e.key)== key ||(key!= null && key.equals(k))){modcount ++;サイズ - ; if(prev == e)table [i] = next; else prev.next = next; E.RecordRemoval(This); eを返す; } prev = e; e = next; } return e; }削除メソッドを呼び出すとき、最初にオブジェクトのハッシュコード値を使用してオブジェクトを見つけてから削除することがわかります。この問題は、R3オブジェクトのy属性の値を変更したためです。また、rectObjectオブジェクトのハッシュコードメソッドが操作に参加しているため、R3オブジェクトのハッシュコードが変更されているため、R3は削除メソッドには見つかりませんでした。つまり、R3のハッシュコードが変更されましたが、保存する場所は更新されておらず、まだ元の場所にあります。そのため、新しいハッシュコードを使用して見つけた場合、間違いなく見つかりません。
実際、上記の方法は非常に簡単に実装できます。図に示すように:
非常に単純な線形ハッシュテーブル、使用されるハッシュ関数はmod、ソースコードは次のとおりです。
/***ハッシュコードhのインデックスを返します。 */ static int indexfor(int h、int length){return h&(length-1); }これは実際にはMOD操作ですが、この種の操作は%操作よりも効率的です。
1,2,3,4,5はmodの結果を意味し、各要素はリンクリスト構造に対応します。したがって、エントリ<k、v>を削除する場合は、リンクリストのヘッダーノードを取得し、リンクリストを繰り返して、最初にハッシュコードを取得します。 HashCodeとEqualsが等しい場合は、この要素を削除します。
上記のメモリリークはメッセージを教えてくれます。オブジェクトの属性値のハッシュコード操作に参加した場合、削除すると属性値を変更することはできません。そうしないと、深刻な問題が発生します。
実際、ハッシュコードメソッドを調べて、8つの基本データ型に対応するオブジェクトタイプのメソッドに等しい。
その中で、8の基本タイプのハッシュコードは、数値サイズを直接返すのが非常に簡単です。文字列オブジェクトは複雑な計算方法を使用していますが、この計算方法により、この文字列の値が等しい場合、ハッシュコードが等しくなるようにします。 8つの基本タイプの等しい方法は、数値値を直接比較することであり、文字列型はメソッドと弦の値を比較します。
上記はこの記事のすべての内容です。みんなの学習に役立つことを願っています。誰もがwulin.comをもっとサポートすることを願っています。