IEnumerable<T>上的延迟OrderBy

3

我有这样一段代码,它可以根据整数中每个数字的数量对整数的IEnumerable进行排序。

var ints = new List<int>() { 66, 7, 9, -5, -22, 67, 122, -333, 555, -2 };
var ordered = ints.OrderBy(x =>
{
   x = Math.Abs(x);

   Console.WriteLine($"Getting length of {x}");

   int len = 0;
   while (x >= 1)
   {
      len++;
      x /= 10;
   }
   return len;
});

   Console.WriteLine("After OrderBy");
   Console.WriteLine("Fetching first item in ordered sequence");
   Console.WriteLine($"First item is {ordered.First()}");

   Console.WriteLine(string.Join(" ", ordered));

因此,当程序在获取ordered序列中的第一项时遇到行时,在那一刻,IEnumerable正在被排序(我收到输出行Getting length of XXX),因为OrderBy是延迟执行的,这很清楚。
但是,为什么当程序运行Console.WriteLine(string.Join(" ", ordered));时我又再次收到这个输出呢?IEnumerable再次排序了吗?(它不是已经排序了吗?)

我知道你想保持整数为整数,但是通过 x.ToString().Length 测量该数字的长度会更容易和直接。 :) - Alex
我知道这段代码有多愚蠢,但当时已经很晚了。 如果我按 Math.Abs(x) 进行排序,结果几乎一样的。 - monoh_
2个回答

4
当你给ordered赋值时,它们实际上并没有被排序。它只是一个对列表进行排序的指令。IOrderedEnumerable<int>。所以每次尝试访问第一个或任何其他项时,它都会将其转换为一个有序列表,以便提供该项。第一次运行.First()时和第二次运行string.Join时,在两种情况下程序将创建2个不同的有序列表实例,使用它们,然后通过失去引用来丢弃它们(因为您没有保存它)。

0
如果您需要对Ordered进行一次排序,则需要在其上调用ToList(),这样您将仅看到列表结果一次。由于它是有序的。
var ints = new List<int>() { 66, 7, 9, -5, -22, 67, 122, -333, 555, -2 };
var ordered = ints.OrderBy(x =>
{
    x = Math.Abs(x);

    Console.WriteLine($"Getting length of {x}");

    int len = 0;
    while (x >= 1)
    {
        len++;
        x /= 10;
    }
    return len;
}).ToList();

Console.WriteLine("After OrderBy");
Console.WriteLine("Fetching first item in ordered sequence");
Console.WriteLine($"First item is {ordered.First()}");
Console.WriteLine(string.Join(" ", ordered));

1
最好调用.ToArray(),而不是.ToList()。它稍微快一些,并且可以使用一半的内存。 - Enigmativity
在Enigmativity,我对此并不确定,https://dev59.com/B3NA5IYBdhLWcg3wEZeT。无论哪种方式,在这个例子中差异几乎是微不可察的。 - loneshark99
不要忘记使用 @ 符号而不是字母 At 来发送通知给某人。 - Enigmativity
抱歉,那个键在键盘上不起作用 :( 下次我会记住先搜索它,然后再复制粘贴。 - loneshark99
阅读了您的链接后,我仍然坚持我的观点。.ToList().ToArray() 方法都使用加倍策略来分配存储空间,但是.ToArray() 的效率更高。最后,当.ToArray() 完成时,它会使用闪电般快速的 Array.CopyTo 将工作数组复制到一个完美大小的数组中,从而允许工作大小的数组被 GCed。.ToList() 永远保留工作数组。因此,具有 1,025 个元素的列表将具有支持它的 2,048 个元素的数组。 - Enigmativity

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