.net - Linq - 更好的 `Enumerable.Except()` 运算符(性能和灵活性)?

标签 .net performance linq

让我们假设两个对象集合。我想检索第一个集合中未包含在第二个集合中的对象。

对于原始类型的集合,这很简单:

new[]{1,2,3,4}.Except(new[]{2,3}); //  => {1, 4}

但是如果我想使用更复杂的结构怎么办?在下面的示例中,我想使用 Id 进行比较字段。

class Person { string Name; int Id ; }

var lst1 = new[]{ new Person("Ann", 1), new Person("Bob", 2) };
var lst2 = new[]{ new Person("Cathy", 3), new Person("Bob", 2) };

嗯,普遍的共识似乎提供了这两种选择:

  • Enumerable.Except()加定制IEqualityComparer<> ,沿着这些思路:

-

class IdComparer: IEqualityComparer<Person> { /* boilerplate Equals(), GetHashCode() */ }

lst1.Except(lst2, new IdComparer())
    .Select(p=>p.Name);              // => { "Ann" } 

这种方法对于定义相等标准来说很麻烦。

  • 使用否定 .Contains() - 仍然需要 IEqualityComparer<> ;或否定的 .Any() - 这允许指定内联条件。

-

from p1 in lst1
where ! lst2.Any(p2 => p1.Id == p2.Id)
select p1.Name;                      // => { "Ann" } 

这更容易使用,但它读起来就像“对于 lst1 中的每个元素检查 lst2 中的每个元素”,看起来复杂度为 O(M*N)。不确定不同的 Linq 提供商是否可以对此进行优化。

就复杂性而言,.Except()方法的效果要好一些:大约 O(M+N), as it uses a Set<> .

  • Sql 中的“left-outer-join-filtered-by-NULL”技巧怎么样?我没有找到对此的引用,所以要么是我搜索不够,要么是它有缺陷。

-

from p1 in lst1
join p2 in lst2 on p1.Id equals p2.Id into grp
where ! grp.Any()
select p1.Name;                     // => { "Ann" }

这可以使用字段轻松进行比较。
另外,据我所知(深入研究 Enumerable.JoinIterator() 实现),复杂度仍然大致为 O(M+N)。

这是 Enumerable.Except() 的良好替代品吗? ?

最佳答案

您可以使用ExceptBy moreLINQ 库的扩展方法

它允许您指定用于比较的键:

public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    Func<TSource, TKey> keySelector)

甚至指定相等比较器:

public static IEnumerable<TSource> ExceptBy<TSource, TKey>(this IEnumerable<TSource> first,
    IEnumerable<TSource> second,
    Func<TSource, TKey> keySelector,
    IEqualityComparer<TKey> keyComparer)

关于.net - Linq - 更好的 `Enumerable.Except()` 运算符(性能和灵活性)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17167464/

相关文章:

c# - 如何使用表达式生成此属性实现而不是发出 IL?

c# - .NET 有什么好的分布式代理/服务模型吗?

.net - 为什么设置后在 dot net 快捷方式中禁用了目标?

performance - Hadoop中的UDF优化

c - C语言中的缓存性能(关于循环)

c# - LINQ 为什么 "Enumerable = Enumerable.Skip(N)"慢?

c# - 在 C# 中进行线程工作的最佳方法是什么?

c# - .net 标准是否像 .net Core 一样跨平台?

c - 什么时候应该忽略关键部分以及什么时候不需要等待?开放式MP

c# - 为什么 lambda 中的短路不起作用?