ILookup <tkey,tval =“”>与IGrouping <tkey,tval =“”> </tkey,> </tkey,>

时间:2009-08-26 21:16:32

标签: c# linq group-by igrouping ilookup

我一直无法阐明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. 上进行迭代时所获得的序列
  4. ILookup<TKey, TVal>更像是IDictionary<TKey, TVal>,其中值实际上是一系列元素

3 个答案:

答案 0 :(得分:71)

是的,所有这些都是正确的。

ILookup<TKey, TValue>也扩展了IEnumerable<IGrouping<TKey, TValue>>,因此您可以遍历所有键/集合对,也可以(或代替)查找特定键。

我基本上认为ILookup<TKey,TValue>就像IDictionary<TKey, IEnumerable<TValue>>

请记住,ToLookup是“立即执行”操作(立即执行),而GroupBy是延迟的。实际上,通过“拉LINQ”的工作方式,当你从IGrouping的结果开始拉GroupBy时,它必须无论如何都要读取所有数据(因为你无法切换)在其他实现中,它可能能够产生流式传输结果。 (它在Push LINQ中;我希望LINQ to Events是相同的。)

答案 1 :(得分:8)

ILookup和IDictionary之间还有另一个重要区别:前者强制执行不变性,因为这里没有更改数据的方法(除非消费者执行显式转换)。相比之下,IDictionary有像“添加”这样的方法,允许更改数据。因此,从功能编程和/或并行编程的角度来看,ILookup更好。 (我只希望有一个版本的ILookup只为一个键而不是一个组分配一个值。)

(顺便说一下,似乎值得指出IEnumerable和IList之间的关系有点类似于ILookup和IDictionary之间的关系 - 前者是不可变的,后者不是。)

答案 2 :(得分:0)

GroupByToLookUp具有几乎相同的功能除了,这是Reference

  

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进行分组。我这里有两种方法。使用GroupByToLookUp。如前所述,如果我使用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都返回不同的类型。

您可以使用ToDictionary而不是ToLookUp,但是您需要注意以下事项:({reference

  

ToLookup()的用法与ToDictionary()的用法非常相似,   两者都允许您指定键选择器,值选择器和   比较器。主要区别在于ToLookup()允许(和   期望)重复的键,而ToDictionary()不会