我有两个版本的按项目列表分组
List<m_addtlallowsetup> xlist_distincted = xlist_addtlallowsetups.DistinctBy(p => new { p.setupcode, p.allowcode }).OrderBy(y => y.setupcode).ThenBy(z => z.allowcode).ToList();
和分组依据
List <m_addtlallowsetup> grouped = xlist_addtlallowsetups.GroupBy(p => new { p.setupcode, p.allowcode }).Select(grp => grp.First()).OrderBy(y => y.setupcode).ThenBy(z => z.allowcode).ToList();
在我看来,这两个人是一样的,但是必须由外行来解释他们的区别,表现和劣势
答案 0 :(得分:4)
让我们首先回顾MoreLinq
API,以下是DistinctBy
的代码:
public static IEnumerable<TSource> DistinctBy<TSource, TKey>(this IEnumerable<TSource> source,
Func<TSource, TKey> keySelector, IEqualityComparer<TKey> comparer)
{
if (source == null) throw new ArgumentNullException(nameof(source));
if (keySelector == null) throw new ArgumentNullException(nameof(keySelector));
return _(); IEnumerable<TSource> _()
{
var knownKeys = new HashSet<TKey>(comparer);
foreach (var element in source)
{
if (knownKeys.Add(keySelector(element)))
yield return element;
}
}
}
HashSet<T>
只是检查第一个匹配项并返回与Key相匹配的Type T
的第一个元素,其余的都被忽略了,因为Key已经添加到HashSet中了。Func<TSource, TKey> keySelector
定义的集合中每个唯一Keyin有关的第一个元素的最简单方法public static IEnumerable<IGrouping<TKey, TElement>> GroupBy<TSource, TKey, TElement>(this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector) {
return new GroupedEnumerable<TSource, TKey, TElement>(source, keySelector, elementSelector, null);
}
internal class GroupedEnumerable<TSource, TKey, TElement> : IEnumerable<IGrouping<TKey, TElement>>
{
IEnumerable<TSource> source;
Func<TSource, TKey> keySelector;
Func<TSource, TElement> elementSelector;
IEqualityComparer<TKey> comparer;
public GroupedEnumerable(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer) {
if (source == null) throw Error.ArgumentNull("source");
if (keySelector == null) throw Error.ArgumentNull("keySelector");
if (elementSelector == null) throw Error.ArgumentNull("elementSelector");
this.source = source;
this.keySelector = keySelector;
this.elementSelector = elementSelector;
this.comparer = comparer;
}
public IEnumerator<IGrouping<TKey, TElement>> GetEnumerator() {
return Lookup<TKey, TElement>.Create<TSource>(source, keySelector, elementSelector, comparer).GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
LookUp
数据结构将给定键的所有数据分组MoreLinq - DistinctBy
仅实现Enumerable - GroupBy
可以实现的一小部分。如果您的用例是特定的,请使用More Linq API MoreLinq - DistinctBy
的限制,速度会更快,因为与Enumerable - GroupBy
不同,DistinctBy
不会先汇总所有数据,然后为每个数据首先选择唯一的密钥,MoreLinq API只会忽略第一条记录之外的数据MoreLinq
是一个更好的选择。这是Linq中的经典案例,其中多个API可以提供相同的结果,但是我们需要警惕成本因素,因为GroupBy
的目的是要比您期望的任务更广泛DistinctBy
答案 1 :(得分:1)
GroupBy
将导致一个包含关键字(分组条件)及其值的“组”。这就是为什么您需要首先进行Select(grp => grp.First())
的原因。
您可能会怀疑MoreLinq只是它的简写。通过the source的MoreLinq,DistinctBy
实际上是在内存中完成的,方法是为HashSet
选择新的每个项目。 HashSet#Add
将添加项,如果它是HashSet
的新元素,则返回true,然后yield
会将新添加的元素返回到可枚举中。
基于上述差异,您可以说先做GroupBy
然后用Select
进行投影是更安全的方法,因为如果您使用的是Entity Framework(或Linq2Sql,我想)。能够转换为SQL命令是减少应用程序负担并将操作委托给数据库服务器的巨大优势。
但是,您必须了解,实体框架中的GroupBy
实际上使用了被认为是复杂操作的OUTER JOIN
,在某些情况下,它可能会导致您的查询立即被删除。这种情况很少见,即使我抛出的查询也有很多列,大约使用了四个GroupBy
,一堆排序和Where
。
粗略地说,当处理一个已经存在于内存中的枚举时。运行GroupBy
然后运行Select
可能最终需要通过两个操作来迭代您的枚举。直接使用MoreLinq的DistinctBy
可以节省一些时间,因为它保证是由HashSet支持的单个操作,如 Mrinal Kamboj 答案所述,并针对源代码进行了深入分析。