自作クラスで正しく動作させるにはどうしたらいいの?
HashSetとは
HashSetとは値の重複を許可しないセットのことです。
例えば、[1, 2, 3, 3, 4, 5]という6つの数字を格納した配列をHashSetで管理した時、「3」が重複しているので二つ目の「3」はセットには含まれません。
具体的に実装してみたものが以下の通りです。
static void Main(string[] args) { int[] nums = new int[] { 1, 2, 3, 3, 4, 5 }; Console.WriteLine("-----int[]の場合-----"); foreach (var num in nums) { Console.WriteLine(num); } HashSet<int> numsSet = new HashSet<int> { 1, 2, 3, 3, 4, 5 }; Console.WriteLine("-----HashSetの場合-----"); foreach (var num in numsSet) { Console.WriteLine(num); } }
このコードの実行結果は以下の通りです。
-----int[]の場合----- 1 2 3 3 4 5 -----HashSetの場合----- 1 2 3 4 5
このように重複する「3」という要素がHashSetの場合は1つだけになっているのが分かります。
HashSetの定義などに関する詳細は以下のMicrosoftの公式ドキュメントを参考にしてください。
HashSetの仕組み
では、HashSetがどうやって重複を検知しているかを説明します。
- GetHashCodeメソッドをもとにハッシュ値を比較する
- Equalsメソッドでオブジェクトの同値性を調べる
この2ステップを経て、重複かどうかを判定しています。
Objectクラスについて
C#全てのオブジェクトの基底クラスにObjectクラスがあります。そのObjectクラスにはハッシュ値を求めるためのGetHashCode()メソッドと、オブジェクトの同値性を判定するEqualsメソッドが定義されています。また同一性を判定するためのReferenceEqualsメソッドなども定義されています。 Objectクラスについては以下の公式ドキュメントを参考にしてください。
- GetHashCode()をもとにハッシュ値を比較する
- Equalsメソッドでオブジェクトの同値性を調べる
とは、
- 自作クラスで正しくGetHashCode()をオーバーライドする
- 自作クラスで正しくEqualsメソッドをオーバーライドする
ということになります
GetHashCodeをオーバーライド
まず自作クラスとして「ユーザ名」と「ユーザID」を管理するUserクラスを定義します。
public class User { public User(string name, string id) { Name = name; ID = id; } public string Name { get; private set; } public string ID { get; private set; } }
次にGetHashCode()をオーバーライドしていきます。今回は、「ユーザ名」と「ユーザID」が同一である場合に同じハッシュ値になるように実装します。
public override int GetHashCode() { return Name.GetHashCode() ^ ID.GetHashCode(); }
次にEqualsメソッドもオーバーライドします。
public override bool Equals(object obj) { User user = obj as User; if (user == null) return false; return Name == user.Name && ID == user.ID; }
これでHashSetで正しく動作する自作クラスが作成できました。
動作確認
public static void Test() { HashSet<User> users = new HashSet<User> { new User("John", "0"), new User("Mike", "0"), new User("Sam", "0"), new User("Mike", "0") }; foreach (var user in users) { Console.WriteLine(user.Name); } }
実行結果は以下の通りになります。
John Mike Sam