6

hashCode と equals を使用する際の一般的なベスト プラクティスについて他にも質問があることは承知していますが、非常に具体的な質問があります。

インスタンス変数として同じクラスの配列を持つクラスがあります。より明確にするために、コードは次のとおりです。

Class Node{
    Node arr[] = new Node[5];
}

Node クラスの hashCode を上書きする必要があります。配列は、2 つの Node が同じかどうかを判断する上で重要な決定要因です。配列を hashCode の計算に効果的に組み込むにはどうすればよいですか?

- 編集 -

2 つのノードが同じかどうか、つまり、同じ数の子があり、それらの子がまったく同じ状態になるかどうかを確認しようとしています。したがって、2 つのノードでサブツリーを効果的に比較しようとしています。この等価チェックを行うためにハッシュを使用できるかどうか疑問に思っています。

実際にはサブツリー全体をハッシュする必要があると思いますが、クラス定義の再帰的な性質を考えると、それをどのように行うべきかわかりません。

4

5 に答える 5

4

hashCode()実装の一部としてhttp://download.oracle.com/javase/6/docs/api/java/util/Arrays.html#hashCode(java.lang.Object [])を含めます。

于 2011-05-04T17:35:01.873 に答える
2

2つのノードが同じであるかどうか、つまり、同じ数の子があり、それらの子がまったく同じ状態になるかどうかを確認しようとしています。したがって、私は2つのノードのサブツリーを効果的に比較しようとしています。ハッシュを使用してこの同等性チェックを実行できるかどうか疑問に思っています。

いいえ、同等性をチェックするためにハッシュを使用しないでください。それはその目的ではありません。最終的には、オブジェクトが等しくないかどうかを確認するのに役立ちますが、オブジェクトが等しいかどうかはわかりません。

同じオブジェクトは同じハッシュ値を生成しますが、等しくない2つの異なるオブジェクトも同じハッシュを生成できます。つまり、ハッシュ値が異なる場合は、オブジェクトが異なることを確実に知っています。それでおしまい。

同等性をテストする場合は、同等性を実装する必要があります。あなたの場合、あなたのメソッドが再帰的になり、スタックオーバーフローを引き起こす危険があります。オブジェクトにそれ自体への参照が含まれている場合はどうなりますか?

ハッシュを生成する場合は、配列のサイズ(およびそれがnullであるかどうか)を考慮に入れることができますが、可能性があるため、配列内のオブジェクトのハッシュ値を使用しようとはしません。無限ループ。完璧ではありませんが、十分です。

良い結果をもたらすかもしれない別の根本的な方法もあります。ハッシュ値を動的に計算する代わりに、ノードオブジェクトインスタンスごとにランダムなint値を設定します(つまり、作成時に一度だけ、常にその値を返します)。あなたの場合、配列内のオブジェクトインスタンスのハッシュ値を取得することで無限ループのリスクを冒すことはありません。

ハッシュが等しい場合は、配列オブジェクトインスタンスの比較を開始する必要があります。

REM:ノードに他の属性が含まれている場合は、これらの他の属性のハッシュを計算し、配列を忘れてください。ハッシュが2つのオブジェクト間で同一である場合に限り、配列のコンテンツ/サイズについて調査を開始します。

REM2:コメントにはDAGグラフが記載されています。これは、再帰性の問題が発生しないことを意味します。ただし、その条件は、deepHashCode()が成功することを保証するのに十分ではありません。さらに、それもやり過ぎでしょう。この問題を解決するためのより効率的な方法があります。

Nodeが使用するハッシュメソッドが配列のみを使用してハッシュ値を計算する場合、deepHashCode()が機能する可能性があります。しかし、それは効率的ではありません。ハッシュメソッドが他のノード属性を使用する場合、これらの属性も等しくなければなりません。

ノードの同等性を比較するより高速な方法があります。各ノードインスタンスに一意の番号を付けます。次に、2つのノードを比較するには、最初にそれらの配列サイズを比較します。等しい場合は、一意の番号を使用して各アレイのノードを比較します。一方の配列にもう一方のノードがない場合は、等しいノードを処理していません。このソリューションは、再帰的に実行するよりもはるかに高速です。

于 2011-05-04T18:04:21.163 に答える
1

それは、平等の基準が何であるかによって異なります。配列内の順序は重要ですか? もしそうなら、ハッシュコードを配列内のノードの順序に依存させたいと思うでしょう。そうでない場合は、配列内のすべてのノードのハッシュ コードを XOR するなどの操作を行うことができます。おそらく、一部の値は null である可能性があります (そのため注意してください)。

基本的に、 2 つのオブジェクトが等しい場合に同じハッシュ コードを持つようにhashCode、一貫してオーバーライドする必要があります。equalsそれが黄金律です。

Eric Lippert は.NETに関する素晴らしいブログ投稿をGetHashCode行っています。このアドバイスは Java にも同様に当てはまります。

注意すべき潜在的な問題の 1 つ - ノードでサイクルが発生した場合 (ノード A への参照がノード B の配列に表示され、その逆)、ハッシュ コードの計算でもサイクルが発生する可能性があります。

于 2011-05-04T17:35:27.480 に答える
1

Arrays.hashCode()およびArrays.equals()メソッドを使用できます。

于 2011-05-04T17:37:17.450 に答える
0

パフォーマンスが懸念される場合は、現在の回答に追加するいくつかのポイント。

まず、ノード内の子ノードの順序が重要かどうかを決定する必要があります。そうでない場合、配列にハッシュコードを使用できません。で定義されたハッシュコード関数を作成することを検討してくださいjava.util.Set。また、equals のパフォーマンスを向上させるために、内部で何らかの順序付けを使用することも検討してください。たとえば、サブツリーの深さ/高さが異なる場合、深さで並べ替えることができます。

次に、サブツリーが深い場合、ハッシュコードが非常に高価になる可能性があります。したがって、ハッシュコードをキャッシュし、構築時に計算するか (ノードが不変の場合)、または変更時に無効にしてオンデマンドで再計算します。

3 番目に、サブツリーが深い場合は、equals() のハッシュコードをチェックし、早い段階で false を返します。はい、ハッシュコードは Map 実装によって検査されますが、コードが equals() を使用して 2 つのオブジェクトを単純に比較する場所があり、大きな代償を払う可能性があります。

最後に、単純な配列の代わりに Arrays.asList() (子の順序が重要な場合) または HashSet (順序が重要ではなく、2 つの子ノードが等しい場合) の使用を検討してください。次に、equals とハッシュコードは、コンテナー インスタンスへの呼び出しを委譲するように削減されます...もちろん、ハッシュコードの適切なキャッシュを使用します。

于 2011-05-04T19:31:45.913 に答える