1

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

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

这两个在我看来是一样的,但是对于他们的区别,他们的表现和缺点必须有一个外行的解释

4

2 回答 2

5

让我们先回顾一下MoreLinqAPI,下面是代码DistinctBy

MoreLinq - 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>它只检查第一个匹配项并返回与TKey 匹配的 Type 的第一个元素,其余的都被忽略,因为 Key 已经添加到 HashSet
  • 获取与集合中每个唯一 Keyin 相关的第一个元素的最简单方法,如Func<TSource, TKey> keySelector
  • 用例是有限的(GroupBy 可以实现的子集,也可以从您的代码中明确)

可枚举 - GroupBy

源代码

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

概括

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

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

于 2018-09-11T07:52:21.960 回答
3

差异

GroupBy应该产生一个包含键(分组标准)及其值的“组”。这就是为什么你需要先做Select(grp => grp.First())

您可能会怀疑 MoreLinq 只是提供了它的简写形式。MoreLinq的源代码DistinctBy实际上是通过挑选每个新的项目在内存中HashSet完成的。将HashSet#Add添加项目,如果它是 的新元素,则返回 true HashSet,然后yield将新添加的元素返回到可枚举中。

哪一个?

SQL相关

基于上述差异,您可以说使用更安全的方法GroupBy进行投影,因为如果您使用的是实体框架(或 Linq2Sql,我想),它可以转换为 SQL 命令。Select能够被翻译成 SQL 命令是一个很大的优势,可以减少应用程序的负担并将操作委托给数据库服务器。

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

Linq 到对象

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

于 2018-09-11T01:22:40.253 回答