ハッシュ化されたセットの内部がどのように機能するか、HashSet<T>
およびそれらがパフォーマンスを発揮する理由をよりよく理解しようとしています。次の記事を発見し、バケット リストhttp://ericlippert.com/2011/02/28/guidelines-and-rules-for-gethashcode/を使用して簡単な例を実装しました。
私がこの記事を理解している限り (以前もそう思っていました)、バケット リスト自体は、各バケット内の特定の量の要素をグループ化します。1 つのバケットはGetHashCode
、要素で呼び出されるハッシュコードによって表されます。より良いパフォーマンスは、要素よりもバケットが少ないという事実に基づいていると思いました。
今、私は次の素朴なテストコードを書きました:
public class CustomHashCode
{
public int Id { get; set; }
public override int GetHashCode()
{
//return Id.GetHashCode(); // Way better performance
return Id % 40; // Bad performance! But why?
}
public override bool Equals(object obj)
{
return ((CustomHashCode) obj).Id == Id;
}
}
そして、ここでプロファイラー:
public static void TestNoCustomHashCode(int iterations)
{
var hashSet = new HashSet<NoCustomHashCode>();
for (int j = 0; j < iterations; j++)
{
hashSet.Add(new NoCustomHashCode() { Id = j });
}
var chc = hashSet.First();
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int j = 0; j < iterations; j++)
{
hashSet.Contains(chc);
}
stopwatch.Stop();
Console.WriteLine(string.Format("Elapsed time (ms): {0}", stopwatch.ElapsedMilliseconds));
}
私の素朴な考えは、パフォーマンスを向上させるはずのバケットの量を(単純なモジュロで)減らしましょう。しかし、それはひどいものです (私のシステムでは、50000 回の反復で約 4 秒かかります)。また、単純に Id をハッシュコードとして返すと、最終的に 50000 バケットになるため、パフォーマンスが低下するはずだとも考えました。しかし、その逆です。何かを改善するのではなく、単にいわゆる衝突のトーンを生成しただけだと思います。繰り返しになりますが、バケット リストはどのように機能するのでしょうか。