ILookup <TKey,TVal>与IGrouping <TKey,TVal>


76

我一直有麻烦之间阐明的差异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. AnIGrouping<TKey, TVal>是单个组(即键控序列),类似于KeyValuePair<TKey, TVal>其中值实际上是元素序列(而不是单个元素)的情况
  2. 一个IEnumerable<IGrouping<TKey, TVal>>是一系列的序列(类似于您遍历一个IDictionary<TKey, TVal>
  3. AnILookup<TKey, TVal>更像是a IDictionary<TKey, TVal>,其中值实际上是元素序列

Answers:


76

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

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

我基本上认为ILookup<TKey,TValue>是喜欢IDictionary<TKey, IEnumerable<TValue>>

请记住,这ToLookup是“立即执行”操作(立即执行),而aGroupBy是被推迟的。碰巧的是,通过“拉动LINQ”的方式,当您开始IGrouping从a的结果中拉出s时GroupBy,它无论如何都必须读取所有数据(因为您无法在中途切换组),而在其他实现中,能够产生流式传输结果。(在Push LINQ中确实如此;我希望LINQ to Events是相同的。)


谢谢回复。之前我还没有想到过关于Linq的推/拉。当我去Google时,我遇到了您的一个博客,因此我将进行检查。听起来这是一种有趣的思考方式。
mckamey

我仍然认为这些差异不能证明2种不同的界面是合理的。我认为图书馆设计者应该只为一个界面决定。也许只是个人喜好,这在这里还不够明确。
rudimenter 2010年

是我还是与GroupBy vs GroupJoin混淆?这些仅在概念上相关。也许这只是答案中的错字。
sehe 2013年

@sehe:是的,只是一个错字。
Jon Skeet

8

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

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


4
为什么要ILookup只包含一个项目值而不是一个组的值?这Dictionary就是a-或aReadOnlyDictionaryIReadOnlyDictionary如果您希望它是不可变的。
ErikE 2015年

5

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按级别进行分组。我这里有两种方法。使用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

但是,如果我将下面的代码重写如下:(请注意,该代码与之前的代码相同,只不过GroupBy被替换了ToLookUp

 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,但是您需要注意以下几点:(参考资料

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


以1为例。
snr
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.