Linq Orderby 与自身进行比较。为啥?
Posted
技术标签:
【中文标题】Linq Orderby 与自身进行比较。为啥?【英文标题】:Linq Orderby compares with itself. Why?Linq Orderby 与自身进行比较。为什么? 【发布时间】:2021-08-07 14:35:13 【问题描述】:总结:在将 Linq OrderBy 与比较器一起使用时,我看到 OrderBy 将项目与自身进行比较 Compare (x, x)
,并且我看到它多次比较相同的项目 Comparer (x, y)
。
Compare (x, x)
?
为什么 OrderBy 会多次比较同一商品?
问题描述
如果你有一个(可能是空的)项目序列,并且你想要最大的一个,你可以使用OrderBy(...).FirstOrDefault()
。
我想,如果您只使用最大的一件,订购数千件商品会浪费处理能力。您可以尝试通过创建某种Max
方法在一个枚举中找到这个最大的元素。
同样,如果您搜索最大的几个元素:为什么要对所有项目进行排序?
我听到有人说,如果你使用 OrderBy 并且只取第一个元素,那么就不是完整的序列。
所以我想创建一个测试程序,在其中使用客户比较器订购客户。要查看哪些客户是比较者,客户比较者会将客户的 Id 写入控制台。
class Customer
public int Id get; set;
...
class CustomerComparer : Comparer<Customer>
public override int Compare(Customer x, Customer y)
int result = Comparer<int>.Default(x.Id, y.Id);
Console.WriteLine("Compare 0 - 1 => 2", x.Id, y.Id, result);
return result;
控制台程序
static void Main(string[] args)
var customers = new[]
new Customer Id = 2,
new Customer Id = 9,
new Customer Id = 6,
new Customer Id = 1,
new Customer Id = 4,
new Customer Id = 7,
new Customer Id = 3,
new Customer Id = 8,
new Customer Id = 5,
;
IComparer<Customer> comparer = new CustomerComparer;
var result = customers.OrderBy(customer => customer, customerComparer).FirstOrDefault();
如果我运行程序,我会得到以下输出:
Compare 4 - 2 => 1
Compare 4 - 9 => -1
Compare 4 - 5 => -1
Compare 4 - 8 => -1
Compare 4 - 3 => 1
Compare 4 - 6 => -1
Compare 4 - 7 => -1
Compare 4 - 4 => 0
Compare 4 - 1 => 1
Compare 4 - 6 => -1
Compare 4 - 1 => 1
Compare 3 - 2 => 1
Compare 3 - 3 => 0
Compare 3 - 1 => 1
Compare 3 - 4 => -1
Compare 3 - 4 => -1
Compare 3 - 1 => 1
Compare 2 - 2 => 0
Compare 2 - 1 => 1
Compare 4 - 4 => 0
Compare 4 - 3 => 1
Compare 9 - 6 => 1
Compare 9 - 7 => 1
Compare 9 - 9 => 0
Compare 9 - 5 => 1
Compare 9 - 8 => 1
Compare 9 - 9 => 0
Compare 9 - 8 => 1
Compare 7 - 6 => 1
Compare 7 - 7 => 0
Compare 7 - 8 => -1
Compare 7 - 5 => 1
Compare 6 - 6 => 0
Compare 6 - 5 => 1
Compare 7 - 7 => 0
Compare 7 - 8 => -1
Compare 7 - 7 => 0
我看到了一些奇怪的东西:
客户 [4] 与其自身进行了多次比较。这也适用于客户 [7] 和 [6],但不适用于客户 [8] 和 [1] 将客户 [4] 与客户 [6] 进行比较,经过几次比较后,再次将客户 [4] 与客户 [6] 进行比较。 客户 [3] 和 [4] 被比较两次,中间没有任何其他比较。 双重比较也适用于客户 [4] 和 [1],稍后适用于 [4] 和 [3],但不适用于其他客户为什么这是一种高效的排序算法?
【问题讨论】:
对于基于比较的排序算法,比较通常被认为是廉价的(与重新排列元素相反)。当然可以清除冗余比较或缓存它们(以空间换时间),但这通常会降低性能。很少有排序算法能保证最小必要的比较次数,而不是一个数量级。 【参考方案1】:正如 Jeroen Mostert 所提到的,它可能将元素与自身进行比较以使算法更简单,并且在某些情况下,简单性可以提高性能。我希望排序算法得到相当好的优化,所以我不会担心一些额外的比较。另请注意,Orderby
保证是稳定的,这可能会对算法施加额外的限制。
为了解决返回最大值的问题,我建议创建您自己的实现来迭代集合并返回最小/最大。这是相当微不足道的。或者使用类似MoreLinq MaxBy / MinBy
我听说有人说,如果你使用 OrderBy 并且只取第一个元素,那么就不是完整的序列。
OrderBy
的内部运作没有记录。理论上,运行时可以检查整个 linq 调用序列并生成最佳代码。
编辑:
O(n)
(感谢 Matthew Watson 指出这一点)。
在 .Net 框架中,它看起来会创建一个 EnumerableSorter,最终对整个事情进行快速排序,大概是一个稳定的变体。即O(n log n)
在实体框架中,查询应转换为 SQL 并通过查询优化器运行,可能会导致 O(n)
(或更好的)运行时。
【讨论】:
.Net Core 3.x 及更高版本肯定会进行优化,如果您只采用第一项,则它是O(N)
而不是 O(Log(N))
(但仅适用于 Linq-to-objects) .但正如你所说,这没有记录,所以你不能依赖它!
@Matthew Watson,感谢您提供的信息,这对我来说是个新闻。以上是关于Linq Orderby 与自身进行比较。为啥?的主要内容,如果未能解决你的问题,请参考以下文章