查找 与 分组 (ILookup vs. IGrouping)

2019-06-21 03:37发布

我一直有麻烦关节之间的差异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>其中值实际上是元素的序列

Answer 1:

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

ILookup<TKey, TValue>还扩展IEnumerable<IGrouping<TKey, TValue>>以便您可以通过遍历所有的键/收集对,以及(或代替)只是希望在特别的钥匙。

我基本上想到ILookup<TKey,TValue>为像IDictionary<TKey, IEnumerable<TValue>>

请记住, ToLookup是“现在就做”操作(立即执行),而一GroupBy被推迟。 恰好,与“拉LINQ”的作品,当你开始拉动方式IGrouping从的结果S GroupBy ,它反正读取所有的数据(因为你无法通过切换中途组),而在其他实现它可能是能够产生流的结果。 (它在推LINQ,我希望的LINQ to事件是一样的。)



Answer 2:

有ILookup和IDictionary中之间的另一个重要区别是:前者强制不变性在这个意义上,这里是改变数据(当消费者进行显式类型转换时除外)没有方法。 相比之下,IDictionary的有像“添加”,让改变数据的方法。 因此,从功能性的编程和/或并行编程的观点来看,ILookup更佳。 (我只希望也有ILookup的版本,只有一个值分配给一个键,而不是一组。)

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



Answer 3:

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(的),都允许您指定键选择,价值选择和comparers。 的主要区别在于,ToLookup()允许(并期望)的重复的键,而ToDictionary()不



文章来源: ILookup vs. IGrouping