我一直有麻烦关节之间的差异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>
我在下面的类比正确吗?
- 一个
IGrouping<TKey, TVal>
是一个单一的基团(即带键序列),类似于KeyValuePair<TKey, TVal>
其中值实际上是元素的序列(而不是单个元件) - 一个
IEnumerable<IGrouping<TKey, TVal>>
就是其中的一个序列(类似于你得到了什么遍历一个时IDictionary<TKey, TVal>
- 一个
ILookup<TKey, TVal>
更像一个IDictionary<TKey, TVal>
其中值实际上是元素的序列
是的,所有这些都是正确的。
而ILookup<TKey, TValue>
还扩展IEnumerable<IGrouping<TKey, TValue>>
以便您可以通过遍历所有的键/收集对,以及(或代替)只是希望在特别的钥匙。
我基本上想到ILookup<TKey,TValue>
为像IDictionary<TKey, IEnumerable<TValue>>
。
请记住, ToLookup
是“现在就做”操作(立即执行),而一GroupBy
被推迟。 恰好,与“拉LINQ”的作品,当你开始拉动方式IGrouping
从的结果S GroupBy
,它反正读取所有的数据(因为你无法通过切换中途组),而在其他实现它可能是能够产生流的结果。 (它在推LINQ,我希望的LINQ to事件是一样的。)
有ILookup和IDictionary中之间的另一个重要区别是:前者强制不变性在这个意义上,这里是改变数据(当消费者进行显式类型转换时除外)没有方法。 相比之下,IDictionary的有像“添加”,让改变数据的方法。 因此,从功能性的编程和/或并行编程的观点来看,ILookup更佳。 (我只希望也有ILookup的版本,只有一个值分配给一个键,而不是一组。)
(顺便说一句,似乎值得指出的是IEnumerable和IList的关系有点类似ILookup和IDictionary中的一个 - 前者是不可改变的,后者不是。)
GroupBy
和ToLookUp
有这个以外几乎相同的功能: 参考
的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
通过自己的水平进行分组。 我这里有两个办法。 使用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
但是,如果我重写如下上面的代码(注意,除了前面的代码,代码是相同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
注: GroupBy
和ToLookUp
都返回不同类型的了。
您可以使用ToDictionary代替ToLookUp,但是你需要注意这个:( 参考 )
ToLookup的使用()非常相似ToDictionary(的),都允许您指定键选择,价值选择和comparers。 的主要区别在于,ToLookup()允许(并期望)的重复的键,而ToDictionary()不