C#において、配列やリストなどのコレクションから重複を取り除いたり、特定の集合演算を行ったりする際、HashSet<T>は非常に強力なクラスです。
特に.NET Framework 4.7.2および.NET Core 2.0以降で利用可能なLINQの拡張メソッドToHashSetを使用することで、既存のコレクションを効率的にHashSet<T>へ変換できます。ここでは、商品管理システムの在庫データを題材に、ToHashSetによる重複排除と、IntersectWithメソッドを用いた共通項目の抽出について解説します。
ToHashSetメソッドの概要
ToHashSetは、IEnumerable<T>インターフェースを実装するコレクション(配列、Listなど)を、重複のない一意な要素のみを持つHashSet<T>に変換するメソッドです。
通常のToListやToArrayと比較した際の主な利点は以下の通りです。
- 自動的な重複排除: 変換の過程で重複する要素が自動的に取り除かれます。
- 検索の高速化: ハッシュベースのルックアップにより、要素の検索(
Containsなど)が高速に行えます。
実践的なコード例:在庫リストの照合
以下のコードは、複数の倉庫から取得した商品IDリストをもとに、重複を整理し、両方の倉庫に存在する共通の商品IDを抽出する例です。
ここでは、重複を含む配列からHashSet<T>を生成し、さらに別のリストとの積集合(共通部分)を求めています。
using System;
using System.Collections.Generic;
using System.Linq;
namespace InventoryManagement
{
class Program
{
static void Main()
{
// シナリオ:
// 倉庫Aと倉庫Bの在庫チェックを行い、両方の倉庫に存在する商品IDを特定する。
// データ入力ミスにより、リストには重複が含まれている可能性がある。
// 倉庫Aの在庫データ(重複あり)
var warehouseAInventory = new[] { 1001, 1002, 1005, 1001, 1003 };
// 倉庫Bの在庫データ(重複なし)
var warehouseBInventory = new[] { 1002, 1003, 1004, 1006 };
// 1. ToHashSetを使用して、倉庫AのデータをHashSet<int>に変換
// この時点で、重複していた "1001" は1つにまとめられます。
HashSet<int> uniqueInventoryA = warehouseAInventory.ToHashSet();
// 2. 比較対象として倉庫BのデータもHashSetに変換(またはそのまま利用)
// ここでは比較用にToHashSetを使用します。
HashSet<int> uniqueInventoryB = warehouseBInventory.ToHashSet();
// 3. IntersectWithを使用して積集合を求める
// uniqueInventoryAの内容が書き換わり、「両方に含まれる要素」のみが残ります。
uniqueInventoryA.IntersectWith(uniqueInventoryB);
// 結果の出力
Console.WriteLine("--- 両方の倉庫に存在する商品ID ---");
foreach (var productId in uniqueInventoryA)
{
Console.WriteLine($"Product ID: {productId}");
}
}
}
}
実行結果
--- 両方の倉庫に存在する商品ID ---
Product ID: 1002
Product ID: 1003
技術的なポイントと注意点
1. LINQのIntersectとHashSetのIntersectWithの違い
C#で集合の共通部分(積集合)を求める方法は主に2つありますが、挙動が異なります。
- IEnumerable.Intersect (LINQ):
- 新しいシーケンス(
IEnumerable<T>)を返します。 - 元のコレクションは変更されません。
- 新しいシーケンス(
- HashSet.IntersectWith:
- 戻り値は
voidです。 - メソッドを呼び出したインスタンス自身を書き換えます。
- メモリ割り当ての観点では、新しいコレクションを生成しないため効率的な場合がありますが、元のデータを保持したい場合は注意が必要です。
- 戻り値は
2. 参照型の扱い
上記の例ではint型(値型)を使用しましたが、自作クラスなどの参照型を扱う場合、ToHashSetや集合演算が正しく動作するためには、そのクラスでGetHashCodeとEqualsメソッドを適切にオーバーライドするか、ToHashSetの引数にIEqualityComparer<T>を渡す必要があります。これを行わない場合、オブジェクトの参照(メモリアドレス)に基づいて比較が行われるため、プロパティの値が同じでも「別のオブジェクト」として扱われる可能性があります。
まとめ
ToHashSetメソッドを使用することで、簡潔な記述でコレクションの重複排除が可能となります。また、変換後のHashSet<T>が持つIntersectWithなどのメソッドを活用することで、複数のデータソース間の照合処理を効率的に実装できます。データの整合性チェックやフィルタリング処理において、積極的に活用すべき機能です。
