C# linq函数OrderByDescending和字符串长度的OrderBy在内部是如何工作的?它比使用循环更快吗?

C# linq函数OrderByDescending和字符串长度的OrderBy在内部是如何工作的?它比使用循环更快吗?,c#,.net,performance,linq,for-loop,C#,.net,Performance,Linq,For Loop,我的问题是基于,我已经发布了关于那个问题的答案 这是代码 var lines = System.IO.File.ReadLines(@"C:\test.txt"); var Minimum = lines[0];//Default length set var Maximum = ""; foreach (string line in lines) { if (Maximum.Length < line.Length) { Maximum = li

我的问题是基于,我已经发布了关于那个问题的答案

这是代码

var lines = System.IO.File.ReadLines(@"C:\test.txt");
var Minimum = lines[0];//Default length set
var Maximum = "";

foreach (string line in lines)
{    
    if (Maximum.Length < line.Length)
    {
        Maximum = line;
    }

    if (Minimum.Length > line.Length)
    {
        Minimum = line;
    }
}
LINQ易于阅读和实现


我想知道哪一个对性能有好处。
以及Linq在内部如何处理OrderByDescending和OrderBy的长度排序问题

在我看来,您需要了解一些要点,以确定什么是最佳方式

首先,我们想用LINQ解决这个问题。然后,要编写最优化的代码,您必须了解延迟执行。大多数Linq方法,如
Select
Where
OrderBy
Skip
Take
,还有一些方法使用DE。那么,什么是延迟执行呢?这意味着,除非用户不需要这些方法,否则不会执行这些方法。这些方法只会创建迭代器。当我们需要迭代器时,这个迭代器就可以执行了。那么,用户如何让它们执行呢?答案是,在
foreach
的帮助下,它将调用
GetEnumerator
或其他Linq方法。例如,
ToList()
First()
FirstOrDefault()
Max()

这些过程将帮助我们获得一些性能。
现在,让我们回到你的问题上来。将返回
IEnumerable
,这意味着它不会读取行,除非我们需要它们。在您的示例中,您曾两次调用此对象的排序方法,这意味着它将对此集合重新排序两次。相反,您可以对集合进行一次排序,然后调用
ToList()
,它将执行
OrderedEnumerable
迭代器,然后获取集合的第一个和最后一个元素,这些元素实际上在我们的手中

var orderedList = lines
   .OrderBy(a => a.Length) // This method uses deferred execution, so it is not executed yet
   .ToList(); // But, `ToList()` makes it to execute.

var Maximum = orderedList.Last();
var Minimum = orderedList.First();
顺便说一句,你可以找到
OrderBy
源代码

返回实例,排序算法如下:

public IEnumerator<TElement> GetEnumerator() 
{
    Buffer<TElement> buffer = new Buffer<TElement>(source);
    if (buffer.count > 0) 
    {
        EnumerableSorter<TElement> sorter = GetEnumerableSorter(null);
        int[] map = sorter.Sort(buffer.items, buffer.count);
        sorter = null;
        for (int i = 0; i < buffer.count; i++) yield return buffer.items[map[i]];
    }
}
public IEnumerator GetEnumerator()
{
缓冲区=新缓冲区(源);
如果(buffer.count>0)
{
EnumerableSorter-sorter=GetEnumerableSorter(null);
int[]map=sorter.Sort(buffer.items,buffer.count);
分拣机=空;
对于(inti=0;i
现在,让我们回到影响性能的另一个方面。如您所见,Linq使用另一个元素来存储已排序的集合。当然,这需要一些记忆,这告诉我们这不是最有效的方法

我只是想告诉你Linq是怎么工作的。但是,我非常同意@Dotctor对您的总体回答。只是,不要忘记,您可以使用
File.ReadAllLines
,它不会返回
IEnumerable
,而是
string[]

这是什么意思?正如我在开始时试图解释的那样,不同之处在于,如果它是
IEnumerable
,那么当Enumerator通过迭代器枚举时,.net将逐行读取。但是,如果它是
string[]
,那么应用程序内存中的所有行。

最有效的方法是避免LINQ。在这里,使用
foreach
的方法只需要一个枚举

如果要将整个文件放入一个集合中,可以使用以下方法:

List<string> orderedLines = System.IO.File.ReadLines(@"C:\test.txt")
    .OrderBy(l => l.Length)
    .ToList();
string shortest = orderedLines.First();
string longest  = orderedLines.Last();
唯一的缺点是它执行的排序不稳定,而不是
OrderBy
。因此,如果两行的长度相同,则可能无法维持顺序。

可以

停止执行或修改代码。尝试编写正确执行的代码,然后如果以后遇到性能问题,请分析应用程序并查看问题所在。如果您有一段代码由于查找最短和最长的字符串而出现性能问题,那么请开始优化此部分

我们应该忘记小效率,比如说97%的时间: 过早优化是万恶之源。然而,我们不应该通过 在这关键的3%的时间里抓住我们的机会-Donald Knuth


File.ReadLines
正在返回一个
IEnumerable
,这意味着如果您对其执行foreach操作,它将逐个向您返回数据。我认为您在这里可以做的最好的性能改进是改进从磁盘读取文件的能力。如果文件足够小,可以将整个文件加载到内存中,请使用
file.ReadAllLines
,如果文件不够小,请尝试将文件分成适合内存的大块读取。逐行读取文件会由于磁盘的I/O操作而导致性能下降。所以这里的问题不是LINQ或loop如何执行,问题在于磁盘读取的数量。

使用第二种方法,您不仅要对行进行两次排序。。。您正在读取文件两次。这是因为
File.ReadLines
返回一个
IEnumerable
。这清楚地说明了为什么除非您知道如何构建
IEnumerable
,否则永远不应该将它枚举两次。如果确实要执行此操作,请添加一个
.ToList()
或一个
.ToArray()
,将
IEnumerable
具体化到集合中。。。虽然第一种方法的内存占用量只有一行文本(因为它一次读取一行文件),但第二种方法将在内存中加载整个文件以对其进行排序,因此内存占用量将大得多,如果文件大小约为数百mb,差异将大得多(请注意,从技术上讲,您可以拥有一个文本长度为1gb的文件,因此此规则不是绝对的……它适用于具有长达数百个字符的行的合理文件:-))

现在。。。有人会告诉你过早优化是邪恶的,但我会告诉你无知是邪恶的两倍

如果您知道这两个代码块之间的差异,那么您可以在t
List<string> orderedLines = System.IO.File.ReadLines(@"C:\test.txt")
    .OrderBy(l => l.Length)
    .ToList();
string shortest = orderedLines.First();
string longest  = orderedLines.Last();
string[] allLines = System.IO.File.ReadAllLines(@"C:\test.txt"); 
Array.Sort(allLines, (x, y) => x.Length.CompareTo(y.Length));
string shortest = allLines.First();
string longest  = allLines.Last();