MoreLINQ的DistinctBy和Linq的GroupBy有什么区别

时间:2018-09-11 00:32:19

标签: c# linq morelinq

我有两个版本的按项目列表分组

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();

在我看来,这两个人是一样的,但是必须由外行来解释他们的区别,表现和劣势

2 个答案:

答案 0 :(得分:4)

让我们首先回顾MoreLinq API,以下是DistinctBy的代码:

MoreLinq-与众不同

Source Code

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有关的第一个元素的最简单方法
  • 用例有限(GroupBy可以实现的子集,也可以从您的代码中清除)

可枚举-GroupBy

Source Code

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数据结构将给定键的所有数据分组
  • 为通过投影的元素和结果选择提供灵活性,从而能够满足许多不同的用例

摘要

  1. MoreLinq - DistinctBy仅实现Enumerable - GroupBy可以实现的一小部分。如果您的用例是特定的,请使用More Linq API
  2. 在您的用例中,由于范围MoreLinq - DistinctBy的限制,速度会更快,因为与Enumerable - GroupBy不同,DistinctBy不会先汇总所有数据,然后为每个数据首先选择唯一的密钥,MoreLinq API只会忽略第一条记录之外的数据
  3. 如果需求是特定的用例,并且不需要数据投影,那么MoreLinq是一个更好的选择。

这是Linq中的经典案例,其中多个API可以提供相同的结果,但是我们需要警惕成本因素,因为GroupBy的目的是要比您期望的任务更广泛DistinctBy

答案 1 :(得分:1)

差异

GroupBy将导致一个包含关键字(分组条件)及其值的“组”。这就是为什么您需要首先进行Select(grp => grp.First())的原因。

您可能会怀疑MoreLinq只是它的简写。通过the source的MoreLinq,DistinctBy实际上是在内存中完成的,方法是为HashSet选择新的每个项目。 HashSet#Add将添加项,如果它是HashSet的新元素,则返回true,然后yield会将新添加的元素返回到可枚举中。

哪个?

与SQL相关

基于上述差异,您可以说先做GroupBy然后用Select进行投影是更安全的方法,因为如果您使用的是Entity Framework(或Linq2Sql,我想)。能够转换为SQL命令是减少应用程序负担并将操作委托给数据库服务器的巨大优势。

但是,您必须了解,实体框架中的GroupBy实际上使用了被认为是复杂操作的OUTER JOIN,在某些情况下,它可能会导致您的查询立即被删除。这种情况很少见,即使我抛出的查询也有很多列,大约使用了四个GroupBy,一堆排序和Where

Linq到对象

粗略地说,当处理一个已经存在于内存中的枚举时。运行GroupBy然后运行Select可能最终需要通过两个操作来迭代您的枚举。直接使用MoreLinq的DistinctBy可以节省一些时间,因为它保证是由HashSet支持的单个操作,如 Mrinal Kamboj 答案所述,并针对源代码进行了深入分析。