【问题标题】:ILookup<TKey, TVal> vs. IGrouping<TKey, TVal>ILookup<TKey, TVal> 与 IGrouping<TKey, TVal>
【发布时间】:2009-08-26 21:16:31
【问题描述】:

我一直难以表达ILookup&lt;TKey, TVal&gt;IGrouping&lt;TKey, TVal&gt; 之间的区别,我很好奇我现在是否理解正确。 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&lt;TKey, TVal&gt; 是单个组(即键控序列),类似于 KeyValuePair&lt;TKey, TVal&gt;,其中值实际上是元素序列(而不是单个元素)
  2. IEnumerable&lt;IGrouping&lt;TKey, TVal&gt;&gt; 是这些序列的序列(类似于迭代 IDictionary&lt;TKey, TVal&gt; 时得到的结果)
  3. ILookup&lt;TKey, TVal&gt; 更像是IDictionary&lt;TKey, TVal&gt;,其中的值实际上是一个元素序列

【问题讨论】:

    标签: c# linq group-by igrouping ilookup


    【解决方案1】:

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

    并且ILookup&lt;TKey, TValue&gt; 还扩展了IEnumerable&lt;IGrouping&lt;TKey, TValue&gt;&gt;,因此您可以迭代所有键/​​集合对以及(或代替)仅查找特定键。

    我基本上认为ILookup&lt;TKey,TValue&gt; 就像IDictionary&lt;TKey, IEnumerable&lt;TValue&gt;&gt;

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

    【讨论】:

    • 感谢您的回复。我以前没有在推/拉方面考虑过 Linq。当我去谷歌时,我看到了你的一个博客,所以我会去看看。听起来是一种有趣的思考方式。
    • 我仍然认为这些差异并不能证明 2 个不同的接口是合理的。我认为图书馆设计师应该决定只使用一个界面。也许只是个人口味,这里的东西不够明确。
    • 是我还是 GroupBy 与 GroupJoin 有什么混淆?这些只是在概念上相关。也许这只是答案中的一个错字。
    【解决方案2】:

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

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

    【讨论】:

    • 您为什么想要一个只有单个项目值而不是组的ILookup?这就是 Dictionary 的含义——如果您希望它是不可变的,则可以使用 ReadOnlyDictionaryIReadOnlyDictionary
    【解决方案3】:

    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() 没有

    【讨论】:

    • 感谢您明确解释这两者之间的重要区别!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多