使用Min()和Max()函数还是单独使用老式的foreach循环?

5

如果我有一个大型集合并且关心性能,我应该相信奇迹并使用

var min = Y.Min();
var max = Y.Max();

要成为一名优秀的工程师,我最好使用

var max = double.NegativeInfinity;
var min = double.PositiveInfinity;
foreach(var y in Y)
{
    if(y > max)
        max = y;
    if(y < min)
        min = y;
}

YICollection<double>,因为我需要使用 Countforeach。我想知道类型是否正确,因为涉及到最小值/最大值,而且我需要从集合的末尾开始迭代。

Y.OrderByDescending((o) => o)...

3
为什么不尝试两种方法并对它们进行比较?性能差异是否足够显著,以至于需要使用更不直观的代码? - D Stanley
2
你最后的问题非常令人困惑-你为什么需要调用 OrderByDescending - Jon Skeet
@DStanley,我的知识不够扎实。我听说过 Linq 的“魔力”,它可以优化查询或“延迟”某些东西(处理?)。我正在寻找一种解决方案,使 MinMaxOrderByDescending 能够胜过钝的 foreach(如果可能的话)。或者简单确认一下,foreach 是否最好。 - Sinatr
1
调用 MinMaxO(n) + O(n) = O(2n),而循环只是 O(n)。现在问问自己哪个更有效率? - Sriram Sakthivel
1
@Sinatr:如果你最终需要对列表进行排序,为什么还需要额外的for循环呢?First()应该是最大值,而Last()应该是最小值,除非我漏掉了什么... - code4life
显示剩余8条评论
3个回答

5

Linq并没有什么“魔法”可以像那样优化查询。它只是在集合之上添加了迭代器。Linq旨在提高编码效率,而不是原始性能。

可能存在一些情况,Linq查询的执行速度比foreach更快,但这不是其中之一。MinMax是两个单独的操作,因此编译器必须“向前看”,以了解正在进行哪些操作,以确定它们是否可以组合成单个迭代。它并不是那么复杂。


非常直接,我的疑虑已经消除了..我想。 - Sinatr

1

假设YIEnumerable<double>,那么调用例如Y.Max()将会调用以下System.Linq.Enumerable.Max()的重载(注释为我自己添加,源代码反编译自System.Core.dll):

public static double Max(this IEnumerable<double> source)
{
    if (source == null)
    {
        throw Error.ArgumentNull("source");
    }
    double num = 0.0; // current max
    bool flag = false; // is first iteration
    foreach (double num2 in source)
    {
        if (flag)
        {
            if (num2 > num || double.IsNaN(num))
            {
                num = num2;
            }
        }
        else // initialization case
        {
            num = num2;
            flag = true;
        }
    }
    if (flag) // throw if there were no elements
    {
        return num;
    }
    throw Error.NoElements();
}

作为一个通用的经验法则,如果已经存在可以满足您需求的东西-请使用它-除非您观察到任何性能问题,那么您可能需要进行优化。

谢谢。如果它不是IEnumerable呢?有没有更适合我需要的类型? - Sinatr
3
如果不是IEnumerable,那么无论如何都不能使用foreach - dav_i
也许我不应该这样做?正如我所提到的,它现在是 ICollection,这样我就可以拥有 Count 属性(不确定是否必须,也许对于 IEnumerable 来说,linq 的 Count() 方法一样快,但我怀疑计算所有东西的时间复杂度为 O(n))。也许有其他更好的类型,可以让我以某种方式迭代集合(例如,使用 ToEnumerable(),假设这样的调用比最小值、最大值和反向排序更便宜)? - Sinatr
我认为你需要学习一下接口。ICollection<T> 实现了 IEnumerable<T>。一个不错的技巧是在东西上按 F12(假设你正在使用 Visual Studio) - 这将显示定义并允许你查看它实现了哪些接口。 - dav_i
2
@Sinatr,我认为你现在想得太多了。你正在专注于微观优化一个查询,而这个查询在最终程序中可能根本不是问题!你可能有其他元素(UI、数据访问)在处理时间方面远远超过查询。使用能让你最有效率的工具,对程序进行分析,并优先优化最慢的部分。 - D Stanley
显示剩余2条评论

1
你可以尝试使用 Key 和 Value 相同的 SortedList:
var list = new SortedList<double, double> { { 4, 4}, { 9, 9}, { 7, 7} };
var min = list.Keys[0];
var max = list.Keys[list.Count - 1];

第一个值始终是最小值,最后一个是最大值。由于它是升序的,所以对于排序并没有太大帮助。此外,插入不是非常高效,因此如果您关心创建的性能(而不是从中读取),那么它并不是一个很好的选择。


好主意。这就是我所说的其他类型。在我的情况下,对Y(您代码中的列表)进行任何更改都需要重新计算最小值、最大值和命中测试(还不确定),这可能比一开始就使用SortedList要昂贵得多。新项目只会被添加。但是,我将在某个时候开始删除第一个项目,所以仍然不太满意。 - Sinatr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接