インターフェイスIEqualityComparerのGetHashCodeメソッドの役割を理解しようとしています。
次の例は、MSDNからのものです。
using System;
using System.Collections.Generic;
class Example {
static void Main() {
try {
BoxEqualityComparer boxEqC = new BoxEqualityComparer();
Dictionary<Box, String> boxes = new Dictionary<Box,
string>(boxEqC);
Box redBox = new Box(4, 3, 4);
Box blueBox = new Box(4, 3, 4);
boxes.Add(redBox, "red");
boxes.Add(blueBox, "blue");
Console.WriteLine(redBox.GetHashCode());
Console.WriteLine(blueBox.GetHashCode());
}
catch (ArgumentException argEx) {
Console.WriteLine(argEx.Message);
}
}
}
public class Box {
public Box(int h, int l, int w) {
this.Height = h;
this.Length = l;
this.Width = w;
}
public int Height { get; set; }
public int Length { get; set; }
public int Width { get; set; }
}
class BoxEqualityComparer : IEqualityComparer<Box> {
public bool Equals(Box b1, Box b2) {
if (b1.Height == b2.Height & b1.Length == b2.Length
& b1.Width == b2.Width) {
return true;
}
else {
return false;
}
}
public int GetHashCode(Box bx) {
int hCode = bx.Height ^ bx.Length ^ bx.Width;
return hCode.GetHashCode();
}
}
Equalsメソッドの実装は、2つのBoxオブジェクトを比較するのに十分ではありませんか?ここで、オブジェクトの比較に使用されるルールをフレームワークに伝えます。なぜGetHashCodeが必要ですか?
ありがとう。
ルシアン
最初に背景を少し...
.NETのすべてのオブジェクトには、EqualsメソッドとGetHashCodeメソッドがあります。
Equalsメソッドを使用して、1つのオブジェクトを別のオブジェクトと比較し、2つのオブジェクトが同等であるかどうかを確認します。
GetHashCodeメソッドは、オブジェクトの32ビット整数表現を生成します。オブジェクトに含めることができる情報の量に制限はないため、特定のハッシュコードは複数のオブジェクトで共有されます。したがって、ハッシュコードは必ずしも一意ではありません。
辞書は、追加/削除/取得操作の(多かれ少なかれ)一定のコストと引き換えに、より高いメモリフットプリントと引き換えになる、本当にクールなデータ構造です。ただし、繰り返し処理には適していません。内部的に、ディクショナリにはバケットの配列が含まれており、そこに値を保存できます。キーと値を辞書に追加すると、キーでGetHashCodeメソッドが呼び出されます。返されたハッシュコードは、キー/値のペアを保存するバケットのインデックスを決定するために使用されます。
値にアクセスする場合は、キーを再度渡します。 GetHashCodeメソッドがキーで呼び出され、Valueを含むバケットが見つかります。
IEqualityComparerがディクショナリのコンストラクターに渡されると、Keyオブジェクトのメソッドの代わりにIEqualityComparer.EqualsおよびIEqualityComparer.GetHashCodeメソッドが使用されます。
両方の方法が必要な理由を説明するために、この例を考えてみましょう。
BoxEqualityComparer boxEqC = new BoxEqualityComparer();
Dictionary<Box, String> boxes = new Dictionary<Box, string>(boxEqC);
Box redBox = new Box(100, 100, 25);
Box blueBox = new Box(1000, 1000, 25);
boxes.Add(redBox, "red");
boxes.Add(blueBox, "blue");
例でBoxEqualityComparer.GetHashCodeメソッドを使用すると、これらのボックスはどちらも明らかに同じオブジェクトではありませんが、同じハッシュコード-100 ^ 100 ^ 25 = 1000 ^ 1000 ^ 25 = 25を持ちます。この場合、これらが同じハッシュコードである理由は、^(ビットごとの排他的論理和)演算子を使用しているため、1000 ^ 1000と同様に100 ^ 100がゼロを残してキャンセルされるためです。 2つの異なるオブジェクトに同じキーがある場合、それを衝突と呼びます。
同じハッシュコードを持つ2つのキー/値のペアを辞書に追加すると、それらは両方とも同じバケットに保存されます。そのため、Valueを取得する場合は、KeyでGetHashCodeメソッドが呼び出され、バケットが特定されます。バケットには複数の値があるため、辞書は、キーのEqualsメソッドを呼び出して正しい値を見つけるためにバケット内のすべてのキー/値のペアを反復処理します。
投稿した例では、2つのボックスは同等であるため、Equalsメソッドはtrueを返します。この場合、ディクショナリには2つの同一のキーがあるため、例外がスローされます。
[〜#〜] tldr [〜#〜]
したがって、要約すると、GetHashCodeメソッドを使用して、オブジェクトが格納されているアドレスを生成します。そのため、辞書で検索する必要はありません。ハッシュコードを計算し、その場所にジャンプするだけです。 Equalsメソッドは同等性のより良いテストですが、オブジェクトをアドレス空間にマッピングするために使用することはできません。
役立つことを願っています
GetHashCodeは辞書コレクションで使用され、オブジェクトを格納するためのハッシュを作成します。ここに、IEqualtyComparerとGetHashCodeを使用する理由と方法の素敵な記事があります- http://dotnetperls.com/iequalitycomparer
Dictionary<TKey,TValue>
のGetValue
および同様のメソッドが、保存されているすべてのキーでEquals
を呼び出して、検索対象のキーと一致するかどうかを確認することは可能ですが、スロー。代わりに、多くのハッシュベースのコレクションのように、GetHashCode
に依存して、一致しないほとんどの値を考慮から迅速に除外します。求められているアイテムでGetHashCode
を呼び出すと42になり、コレクションには53,917のアイテムがありますが、53,914のアイテムでGetHashCode
を呼び出すと42以外の値が得られ、3つのアイテムだけが必要になります求められているものと比較されます。他の53,914は無視しても安全です。
GetHashCode
がIEqualityComparer<T>
に含まれる理由は、辞書の消費者が通常not互いに等しいと見なします。最も一般的な例は、文字列をキーとして使用したいが、大文字と小文字を区別しない比較を使用する呼び出し元です。これを効率的に機能させるためには、辞書は「Fox」と「FOX」に同じ値をもたらすが、できれば「box」または「zebra」に何か他のものをもたらすハッシュ関数の形式を持つ必要があります。 GetHashCode
に組み込まれたString
メソッドはそのようには機能しないため、辞書はそのようなメソッドを別の場所から取得する必要があり、IEqualityComparer<T>
はそのようなハッシュコードの必要性は、「Fox」と「FOX」を互いに同一であるが「box」または「zebra」とは見なさないEquals
メソッドに非常に強く関連付けられます。