Core源码(七)GroupBy
如果大数据量的分组,而且是从数据库读取,那么分组尽可能在数据库端进行,因为大数据量的读取传输就会消耗一定量的带宽和时间,而在网页应用中,响应时间越短越好,时间越长,用户体验越糟。而在数据库中分组,读取分组后的统计数据,消耗的传输带宽会明显降低。
这里我们介绍的是C#集合的分组,关于数据库的分组可以参考下文
GroupBy使用
基本使用如下
List<IGrouping<long,StatisEmployeeLog>> group=list.GroupBy(c => c.DefeatReasonId).ToList(); var groupList =callList.GroupBy(q => new { q.AgentId, q.CallStartTime.Date }).ToList() ;
分组后的查询
var thisGroup = group.FirstOrDefault(c => c.Key == item.DefeatReasonId); var listThis = thisGroup.ToList();
基本原理
GroupBy在没有传comparer的时候,会创建一个基于当前TSource类型的默认的comparer。
但不管是默认的comparer还是我们自己传的comparer,都会调用Equals和GetHashCode两个方法,所以我们需要重载这两个方法。不论如何,一定要重载GetHashCode。匿名类型会自动生成对应的GetHashCode方法,而我们定义的类,要自己实现。
比如如下
class StudentKey : IEquatable<StudentKey> { public int Age { get; set; } public string Class { get; set; } public override int GetHashCode() { return Age.GetHashCode() ^ Class.GetHashCode(); } public bool Equals(StudentKey other) { return Age == other.Age && Class == other.Class; } }
IGrouping<TKey,TElement>对象序列,请注意,是对象序列,而不是单个对象。由于group查询产生的IGrouping<TKey,TElement>实质上是列表的列表。因此必须使用嵌套的foreach循环来访问每一组的各个子项。外部循环可以访问每个组的Key,内部循环可以访问每个组的子项。每个组的Key可以是任何类型,如字符串、用户自定义的对象或其他
程序通过Enumerable类中如下方法进行分组
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector);
内部调用的逻辑如下
public static IEnumerable<IGrouping<TKey, TSource>> GroupBy<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector) =>
new GroupedEnumerable<TSource, TKey>(source, keySelector, null);
代码位置为corefx\src\System.Linq
GroupedEnumerable类
我们之前使用的tolist方法就是调用的这个类型下的实现
public List<IGrouping<TKey, TSource>> ToList()
{
IIListProvider<IGrouping<TKey, TSource>> lookup = Lookup<TKey, TSource>.Create(_source, _keySelector, _comparer);
return lookup.ToList();
}
Lookup类
Lookup<TKey,TElement>也是一种字典,不过它是一对多,不像Dictionary<TKey,TElement>一样是一对一的。Lookup<int,int>和Dictionary<int,List<int>>是一样的。
Lookup<TKey,TElement>的对象可以存储ToLookup方法的结果。代码如下
ToDictionary 和ToLookUp 对对象集合的操作带来极大的方便,特别是对索引的提供。方便通过 key 来找到相应的键值,ToDictionary 转换成是键值对 关系是一 一 对应的关系且key 值是唯一的不能重复。ToLookUp 是ToDictionary 的扩展版本
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
[DebuggerDisplay("Count = {Count}")] [DebuggerTypeProxy(typeof(SystemLinq_LookupDebugView<,>))] public class Lookup<TKey, TElement> : ILookup<TKey, TElement>, IIListProvider<IGrouping<TKey, TElement>> { private readonly IEqualityComparer<TKey> _comparer; private Grouping<TKey, TElement>[] _groupings; private Grouping<TKey, TElement> _lastGrouping; private int _count; internal static Lookup<TKey, TElement> Create<TSource>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) { Debug.Assert(source != null); Debug.Assert(keySelector != null); Debug.Assert(elementSelector != null); Lookup<TKey, TElement> lookup = new Lookup<TKey, TElement>(comparer); foreach (TSource item in source) { lookup.GetGrouping(keySelector(item), create: true).Add(elementSelector(item)); } return lookup; } internal Grouping<TKey, TElement> GetGrouping(TKey key, bool create) { int hashCode = InternalGetHashCode(key); for (Grouping<TKey, TElement> g = _groupings[hashCode % _groupings.Length]; g != null; g = g._hashNext) { if (g._hashCode == hashCode && _comparer.Equals(g._key, key)) { return g; } } if (create) { if (_count == _groupings.Length) { Resize(); } int index = hashCode % _groupings.Length; Grouping<TKey, TElement> g = new Grouping<TKey, TElement>(); g._key = key; g._hashCode = hashCode; g._elements = new TElement[1]; g._hashNext = _groupings[index]; _groupings[index] = g; if (_lastGrouping == null) { g._next = g; } else { g._next = _lastGrouping._next; _lastGrouping._next = g; } _lastGrouping = g; _count++; return g; } return null; } IGrouping<TKey, TElement>[] IIListProvider<IGrouping<TKey, TElement>>.ToArray() { IGrouping<TKey, TElement>[] array = new IGrouping<TKey, TElement>[_count]; int index = 0; Grouping<TKey, TElement> g = _lastGrouping; if (g != null) { do { g = g._next; array[index] = g; ++index; } while (g != _lastGrouping); } return array; } }
延伸阅读