web-dev-qa-db-ja.com

ILookup <TKey、TVal>とIGrouping <TKey、TVal>

ILookup<TKey, TVal>IGrouping<TKey, TVal> の違いを明確にするのに苦労してきましたが、今正しく理解できているかどうか興味があります。 LINQは、IGroupingアイテムのシーケンスを生成すると同時に、ToLookup拡張メソッドを提供することで、問題を悪化させました。ですから、よく見るまでは同じように感じました。

var q1 = 
    from n in N
    group n by n.MyKey into g
    select g;
// q1 is IEnumerable<IGrouping<TKey, TVal>>

これは次と同等です:

var q2 = N.GroupBy(n => n.MyKey, n => n);
// q2 is IEnumerable<IGrouping<TKey, TVal>>

これはよく似ています:

var q3 = N.ToLookup(n => n.MyKey, n => n);
// q3 is ILookup<TKey, TVal>

私は次の例えで正しいですか?

  1. IGrouping<TKey, TVal>は単一のグループ(つまり、キー付きシーケンス)であり、値が実際には(単一の要素ではなく)要素のシーケンスであるKeyValuePair<TKey, TVal>に類似しています。
  2. IEnumerable<IGrouping<TKey, TVal>>は、それらのシーケンスです(IDictionary<TKey, TVal>を反復処理したときに得られるものと同様です。
  3. ILookup<TKey, TVal>IDictionary<TKey, TVal>に似ており、値は実際には要素のシーケンスです。
73
mckamey

はい、それらはすべて正しいです。

また、ILookup<TKey, TValue>IEnumerable<IGrouping<TKey, TValue>>も拡張するため、特定のキーを検索するだけでなく、すべてのキー/コレクションのペアを反復処理できます。

私は基本的にILookup<TKey,TValue>IDictionary<TKey, IEnumerable<TValue>>のようなものだと思っています。

ToLookupは「今すぐ実行」操作(即時実行)であるのに対し、GroupByは延期されることに注意してください。たまたま、「pull LINQ」の動作方法では、IGroupingの結果からGroupBysをプルし始めると、とにかくすべてのデータを読み取る必要があります(できないため)途中でグループを切り替えます)一方、他の実装では、ストリーミング結果を生成できる場合があります。 (プッシュLINQで実行されます。LINQtoEventsは同じであると思います。)

73
Jon Skeet

ILookupとIDictionaryには、もう1つの重要な違いがあります。前者は、データを変更する方法がないという意味で不変性を強制します(コンシューマーが明示的なキャストを実行する場合を除く)。対照的に、IDictionaryには、データの変更を可能にする「追加」のようなメソッドがあります。したがって、関数型プログラミングや並列プログラミングの観点からは、ILookupの方が優れています。 (グループではなく、キーに1つの値のみを割り当てるバージョンのILookupもあればいいのにと思います。)

(ところで、IEnumerableとIListの関係は、ILookupとIDictionaryの関係にいくぶん似ていることを指摘する価値があるようです。前者は不変ですが、後者は不変です。)

8

GroupByToLookUpの機能はほぼ同じです[〜#〜]ただし[〜#〜]これ:- 参照

GroupBy:GroupBy演算子は、いくつかのキー値に基づいて要素のグループを返します。各グループは、IGroupingオブジェクトによって表されます。

ToLookup:ToLookupはGroupByと同じです。唯一の違いは、GroupByの実行が延期されるのに対し、ToLookupの実行は即時であるということです。

サンプルコードを使用して違いを明確にしましょう。 Personモデルを表すクラスがあるとします。

class Personnel
{
    public int Id { get; set; }
    public string FullName { get; set; }
    public int Level { get; set; }
}

その後、personnelsのリストを次のように定義します。

 var personnels = new List<Personnel>
    {
        new Personnel { Id = 1, FullName = "P1", Level = 1 },
        new Personnel { Id = 2, FullName = "P2", Level = 2 },
        new Personnel { Id = 3, FullName = "P3", Level = 1 },
        new Personnel { Id = 4, FullName = "P4", Level = 1 },
        new Personnel { Id = 5, FullName = "P5", Level =2 },
        new Personnel { Id = 6, FullName = "P6", Level = 2 },
        new Personnel { Id = 7, FullName = "P7", Level = 2 }
    };

次に、personnelsをレベルごとにグループ化する必要があります。ここでは2つのアプローチがあります。 GroupByまたはToLookUpを使用します。前に述べたように、GroupByを使用すると、遅延実行が使用されます。つまり、コレクションを反復処理すると、次のアイテムが呼び出されるまで計算されない場合があります。

 var groups = personnels.GroupBy(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

上記のコードでは、最初にpersonnelsをグループ化しましたが、それを繰り返す前に、いくつかのpersonnelsを削除しました。 GroupByは遅延実行を使用するため、グループ化はここのforeachポイントで計算されるため、最終結果には削除されたアイテムは含まれません。

出力:

2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

しかし、上記のコードを以下のように書き直すと、(コードは前のコードと同じですが、GroupByToLookUpに置き換えられることに注意してください)

 var groups = personnels.ToLookup(p => p.Level);
    personnels.RemoveAll(p => p.Level == 1);
    foreach (var product in groups)
    {
        Console.WriteLine(product.Key);
        foreach (var item in product)
            Console.WriteLine(item.Id + " >>> " + item.FullName + " >>> " + item.Level);
    }

ToLookUpは即時実行を使用するため、ToLookUpメソッドを呼び出すと、結果が生成され、グループが適用されます。したがって、反復前にpersonnelsからアイテムを削除すると、それは最終結果に影響を与えません。

出力:

1
1 >>> P1 >>> 1
3 >>> P3 >>> 1
4 >>> P4 >>> 1
2
2 >>> P2 >>> 2
5 >>> P5 >>> 2
6 >>> P6 >>> 2
7 >>> P7 >>> 2

注:GroupByToLookUpはどちらも異なるタイプを返します。

ToLookUpの代わりにToDictionaryを使用することもできますが、これに注意する必要があります:( 参照

ToLookup()の使用法は、ToDictionary()の使用法と非常に似ており、どちらもキーセレクター、値セレクター、および比較子を指定できます。主な違いは、ToLookup()は重複キーを許可(および期待)するのに対し、ToDictionary()は許可しないことです。

2