Сравнение С# и OrderBy
Я могу сортировать список, используя Sort или OrderBy. Какой из них быстрее? Оба работают на одном и том же
алгоритм?
List<Person> persons = new List<Person>();
persons.Add(new Person("P005", "Janson"));
persons.Add(new Person("P002", "Aravind"));
persons.Add(new Person("P007", "Kazhal"));
1.
persons.Sort((p1,p2)=>string.Compare(p1.Name,p2.Name,true));
2.
var query = persons.OrderBy(n => n.Name, new NameComparer());
class NameComparer : IComparer<string>
{
public int Compare(string x,string y)
{
return string.Compare(x, y, true);
}
}
Ответы
Ответ 1
Почему бы не измерить его:
class Program
{
class NameComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y, true);
}
}
class Person
{
public Person(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; set; }
public string Name { get; set; }
}
static void Main()
{
List<Person> persons = new List<Person>();
persons.Add(new Person("P005", "Janson"));
persons.Add(new Person("P002", "Aravind"));
persons.Add(new Person("P007", "Kazhal"));
Sort(persons);
OrderBy(persons);
const int COUNT = 1000000;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
Sort(persons);
}
watch.Stop();
Console.WriteLine("Sort: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
OrderBy(persons);
}
watch.Stop();
Console.WriteLine("OrderBy: {0}ms", watch.ElapsedMilliseconds);
}
static void Sort(List<Person> list)
{
list.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true));
}
static void OrderBy(List<Person> list)
{
var result = list.OrderBy(n => n.Name, new NameComparer()).ToArray();
}
}
На моем компьютере при компиляции в режиме Release эта программа печатает:
Sort: 1162ms
OrderBy: 1269ms
UPDATE:
Как было предложено @Stefan, здесь приведены результаты сортировки большого списка меньше:
List<Person> persons = new List<Person>();
for (int i = 0; i < 100000; i++)
{
persons.Add(new Person("P" + i.ToString(), "Janson" + i.ToString()));
}
Sort(persons);
OrderBy(persons);
const int COUNT = 30;
Stopwatch watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
Sort(persons);
}
watch.Stop();
Console.WriteLine("Sort: {0}ms", watch.ElapsedMilliseconds);
watch = Stopwatch.StartNew();
for (int i = 0; i < COUNT; i++)
{
OrderBy(persons);
}
watch.Stop();
Console.WriteLine("OrderBy: {0}ms", watch.ElapsedMilliseconds);
Печать
Sort: 8965ms
OrderBy: 8460ms
В этом случае похоже, что OrderBy работает лучше.
UPDATE2:
И используя случайные имена:
List<Person> persons = new List<Person>();
for (int i = 0; i < 100000; i++)
{
persons.Add(new Person("P" + i.ToString(), RandomString(5, true)));
}
Где:
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();
}
Урожайность:
Sort: 8968ms
OrderBy: 8728ms
Еще OrderBy быстрее
Ответ 2
Нет, это не тот же алгоритм. Для начала LINQ OrderBy
документируется как стабильный (т.е. Если два элемента имеют одинаковый Name
, они будут отображаться в исходном порядке).
Это также зависит от того, будет ли вы буферизовать запрос против итерации его несколько раз (LINQ-to-Objects, если вы не буферизируете результат, будет переупорядочивать за foreach
).
Для запроса OrderBy
у меня также возникнет соблазн использовать:
OrderBy(n => n.Name, StringComparer.{yourchoice}IgnoreCase);
(для {yourchoice}
один из CurrentCulture
, Ordinal
или InvariantCulture
).
List<T>.Sort
Этот метод использует Array.Sort, который использует алгоритм QuickSort. Эта реализация нестабильна Сортировать; то есть, если два элемента равный, их порядок может не быть сохранились. Напротив, стабильный вид сохраняет порядок элементов, которые равны.
Enumerable.OrderBy
Этот метод выполняет стабильную сортировку; то есть, если ключи из двух элементов равны, порядок элементов сохраняется. Напротив, нестабильная сортировка не сохраняет порядок элементов, имеющих один и тот же ключ. Сортировать; то есть, если два элемента равный, их порядок может не быть сохранились. Напротив, стабильный вид сохраняет порядок элементов, которые равны.
Ответ 3
Дарин Димитров отвечает, что OrderBy
немного быстрее, чем List.Sort
, когда сталкивается с уже отсортированным вводом. Я изменил его код, чтобы он неоднократно сортировал несортированные данные, а OrderBy
в большинстве случаев несколько медленнее.
Кроме того, в тесте OrderBy
используется ToArray
, чтобы принудительно перечислить перечисление Linq, но это явно возвращает тип (Person[]
), который отличается от типа ввода (List<Person>
). Поэтому я повторил тест, используя ToList
, а не ToArray
, и получил еще большую разницу:
Sort: 25175ms
OrderBy: 30259ms
OrderByWithToList: 31458ms
Код:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
class Program
{
class NameComparer : IComparer<string>
{
public int Compare(string x, string y)
{
return string.Compare(x, y, true);
}
}
class Person
{
public Person(string id, string name)
{
Id = id;
Name = name;
}
public string Id { get; set; }
public string Name { get; set; }
public override string ToString()
{
return Id + ": " + 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 class PersonList : List<Person>
{
public PersonList(IEnumerable<Person> persons)
: base(persons)
{
}
public PersonList()
{
}
public override string ToString()
{
var names = Math.Min(Count, 5);
var builder = new StringBuilder();
for (var i = 0; i < names; i++)
builder.Append(this[i]).Append(", ");
return builder.ToString();
}
}
static void Main()
{
var persons = new PersonList();
for (int i = 0; i < 100000; i++)
{
persons.Add(new Person("P" + i.ToString(), RandomString(5, true)));
}
var unsortedPersons = new PersonList(persons);
const int COUNT = 30;
Stopwatch watch = new Stopwatch();
for (int i = 0; i < COUNT; i++)
{
watch.Start();
Sort(persons);
watch.Stop();
persons.Clear();
persons.AddRange(unsortedPersons);
}
Console.WriteLine("Sort: {0}ms", watch.ElapsedMilliseconds);
watch = new Stopwatch();
for (int i = 0; i < COUNT; i++)
{
watch.Start();
OrderBy(persons);
watch.Stop();
persons.Clear();
persons.AddRange(unsortedPersons);
}
Console.WriteLine("OrderBy: {0}ms", watch.ElapsedMilliseconds);
watch = new Stopwatch();
for (int i = 0; i < COUNT; i++)
{
watch.Start();
OrderByWithToList(persons);
watch.Stop();
persons.Clear();
persons.AddRange(unsortedPersons);
}
Console.WriteLine("OrderByWithToList: {0}ms", watch.ElapsedMilliseconds);
}
static void Sort(List<Person> list)
{
list.Sort((p1, p2) => string.Compare(p1.Name, p2.Name, true));
}
static void OrderBy(List<Person> list)
{
var result = list.OrderBy(n => n.Name, new NameComparer()).ToArray();
}
static void OrderByWithToList(List<Person> list)
{
var result = list.OrderBy(n => n.Name, new NameComparer()).ToList();
}
}
Ответ 4
Я думаю, важно отметить еще одно различие между Sort
и OrderBy
:
Предположим, что существует метод Person.CalculateSalary()
, который занимает много времени; возможно даже больше, чем операция сортировки большого списка.
Сравнение
// Option 1
persons.Sort((p1, p2) => Compare(p1.CalculateSalary(), p2.CalculateSalary()));
// Option 2
var query = persons.OrderBy(p => p.CalculateSalary());
Вариант 2 может иметь превосходную производительность, поскольку он вызывает только метод CalculateSalary
n, тогда как параметр Sort
может вызывать CalculateSalary
до 2n log (n) раз, в зависимости от успеха алгоритма сортировки.
Ответ 5
В двух словах:
List/Array Sort():
- Нестабильная сортировка.
- Сделано на месте.
- Используйте Introsort/Quicksort.
- Пользовательское сравнение осуществляется путем предоставления компаратора. Если сравнение стоит дорого, оно может быть медленнее, чем OrderBy() (что позволяет использовать ключи, см. ниже).
OrderBy/ThenBy():
- Стабильная сортировка.
- Не на месте.
- Используйте Quicksort. Быстрая сортировка не является стабильной сортировкой. Вот хитрость: при сортировке, если два элемента имеют одинаковый ключ, сравнивается их начальный порядок (который был сохранен до сортировки).
- Позволяет использовать ключи (используя лямбды) для сортировки элементов по их значениям (например:
x => x.Id
). Все ключи извлекаются в первую очередь перед сортировкой. Это может привести к лучшей производительности, чем использование Sort() и пользовательского компаратора.
Источники:
MDSN, справочный источник и dotnet/coreclr хранилище (GitHub).
Некоторые из перечисленных выше утверждений основаны на текущей реализации платформы .NET(4.7.2). Это может измениться в будущем.
Ответ 6
вы должны рассчитать сложность алгоритмов, используемых методами OrderBy и Sort.
QuickSort имеет сложность n (log n), как я помню, где n - длина массива.
Я тоже искал orderby, но я не мог найти никакой информации даже в библиотеке msdn.
если у вас нет одинаковых значений и сортировки, связанных только с одним свойством, я предпочитаю использовать
Метод Sort(); если не использовать OrderBy.
Ответ 7
Я просто хочу добавить, что порядок намного полезнее.
Почему? Потому что я могу сделать это:
Dim thisAccountBalances = account.DictOfBalances.Values.ToList
thisAccountBalances.ForEach(Sub(x) x.computeBalanceOtherFactors())
thisAccountBalances=thisAccountBalances.OrderBy(Function(x) x.TotalBalance).tolist
listOfBalances.AddRange(thisAccountBalances)
Почему сложный компаратор? Просто сортировка по полю. Здесь я сортирую на основе TotalBalance.
Очень просто.
Я не могу сделать это с сортировкой. Интересно, почему. Делайте хорошо с заказом.
Что касается скорости, то всегда O (n).