这是对这个优秀问题C# Sort and OrderBy comparison的跟进。我将使用相同的例子:
List<Person> persons = new List<Person>();
persons.Add(new Person("P005", "Janson"));
persons.Add(new Person("P002", "Aravind"));
persons.Add(new Person("P007", "Kazhal"));
争用的方法是:
persons.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true));
//and
persons.OrderBy(n => n.Name);
首先我要说的是,我知道没有任何重大的性能差异需要担心。但我很想知道为什么OrderBy
的表现比Sort
好得多。我正在使用@phoog在原始问题中发布的答案。
private void button1_Click(object sender, EventArgs e)
{
IEnumerable<Person> people;
BenchMark(persons => persons.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true)));
BenchMark(persons => people = persons.OrderBy(n => n.Name));
}
private static Random randomSeed = new Random();
public static string RandomString(int size, bool lowerCase)
{
var sb = new StringBuilder(size);
int start = (lowerCase) ? 97 : 65;
for (int i = 0; i < size; i++)
{
sb.Append((char)(26 * randomSeed.NextDouble() + start));
}
return sb.ToString();
}
private static void BenchMark(Action<List<Person>> action)
{
List<Person> persons = new List<Person>();
for (int i = 0; i < 10000; i++)
{
persons.Add(new Person("P" + i.ToString(), RandomString(5, true)));
}
List<Person> unsortedPersons = new List<Person>(persons);
Stopwatch watch = new Stopwatch();
for (int i = 0; i < 100; i++)
{
watch.Start();
action(persons);
watch.Stop();
persons.Clear();
persons.AddRange(unsortedPersons);
}
MessageBox.Show(watch.Elapsed.TotalMilliseconds.ToString());
}
结果:
Sort() => 3500 ~ 5000 ms
OrderBy() => 0.2 ~ 1.5 ms
虽然我最初测试的列表较小,但差异很大,但一旦收集的大小上升,它就变得越来越明显了。可能是我错过了理解.NET集合的关键,但我的想法是Sort
作用于现有的List<T>
,与{相比}处理时应该有更少的开销(如果每一个) {1}}作用于同一OrderBy
(在我们的案例中为List<T>
),但必须返回另一个集合IOrderedEnumerable<T>
。但是persons
仍然表现得更好。与OrderBy
类型相比,List<T>
可能会有一定的开销,但IEnumerable<T>
无论如何都会对现有列表起作用!此外,我很难看到Sort
方法比现有的.NET方法工作得更快。
原始问题中的所有答案都会将Linq
与Sort
进行比较,我认为这些答案会产生一些开销,因此或多或少会有相同的影响。
实施差异可能是什么?
编辑:好的,我学到了新东西。以下是我对延期执行的确认。
OrderBy.ToList
private void button1_Click(object sender, EventArgs e)
{
BenchMark(persons =>
{
persons.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true));
foreach (var item in persons)
{
break;
}
});
BenchMark(persons =>
{
IEnumerable<Person> people = persons.OrderBy(n => n.Name);
foreach (var item in people)
{
break;
}
});
}
在4000 - 5000毫秒时运行,Sort
在5000毫米以上运行。所以我的结论确实是错的。一旦我开始枚举集合,它们都以平等的条件执行。我更喜欢OrderBy
任何一天的语法:)
编辑2:我刚发现这与this one完全相同。但这里有一个more interesting question about deferred execution in general,但不完全是排序。
答案 0 :(得分:37)
在这种情况下,OrderBy
要快得多,因为你实际上并没有执行它。
在您枚举结果之前,查询 deferred ,因此它实际上从未进行过排序。在您实际枚举结果之前,IOrderedEnumerable<T>
不会处理输入并执行任何形式的排序。
尝试将基准更改为:
BenchMark(persons => people = persons.OrderBy(n => n.Name).Count());
Count()
调用将强制实际进行排序(因为它需要枚举IOrderedEnumerable<T>
以生成计数),这会使您的计时显着均匀。
大多数LINQ扩展方法都是这样工作的 - 直到你枚举它们(通过Count()
,调用ToList()
,或者只是在正常的foreach
循环中使用它们等),它们将具有可忽略不计的影响,因为除了构建可枚举之外,它们实际上并没有直接做任何事情。其他基准与OrderBy(...).ToList()
比较的原因是ToList()
的加法迫使OrderBy
完全执行并实际排序结果。
答案 1 :(得分:12)
OrderBy()
使用延迟执行。
在您枚举其结果之前,它实际上并没有做任何事情。
要正确衡量其效果,您可以拨打.OrderBy(...).Count()
。
答案 2 :(得分:2)
OrderBy()
不会创建排序列表。
它创建一个IEnumerable对象,当您枚举它时,它会生成一个已排序的列表。在枚举列表之前,实际排序不会发生。