Резюме: при использовании Linq OrderBy с компаратором я вижу, что OrderBy сравнивает элементы сами с собой Compare (x, x)
, и я вижу, что он сравнивает одни и те же элементы Comparer (x, y)
несколько раз.
- Почему OrderBy
Compare (x, x)
? - Почему OrderBy сравнивает один и тот же товар несколько раз?
Описание проблемы
Если у вас есть (возможно, пустая) последовательность элементов и вам нужна самая большая последовательность, вы можете использовать OrderBy(...).FirstOrDefault()
.
Я подумал, что заказывать тысячи товаров будет пустой тратой вычислительной мощности, если вы будете использовать только самый крупный из них. Вы можете попытаться найти этот самый большой элемент в одном перечислении, создав какой-то метод Max
.
Точно так же, если вы ищете несколько самых больших элементов: зачем заказывать все элементы?
Я слышал, как кто-то сказал, что если вы используете OrderBy и берете только первый элемент, то упорядочивается не вся последовательность.
Поэтому я решил создать тестовую программу, в которой я буду заказывать клиентов с помощью средства сравнения клиентов. Чтобы увидеть, какие клиенты являются компаратором, он записывает идентификаторы клиентов в консоль.
class Customer
{
public int Id {get; set;}
...
}
class CustomerComparer : Comparer<Customer>
{
public override int Compare(Customer x, Customer y)
{
int result = Comparer<int>.Default(x.Id, y.Id);
Console.WriteLine("Compare {0} - {1} => {2}", x.Id, y.Id, result);
return result;
}
}
Консольная программа
static void Main(string[] args)
{
var customers = new[]
{
new Customer {Id = 2},
new Customer {Id = 9},
new Customer {Id = 6},
new Customer {Id = 1},
new Customer {Id = 4},
new Customer {Id = 7},
new Customer {Id = 3},
new Customer {Id = 8},
new Customer {Id = 5},
};
IComparer<Customer> comparer = new CustomerComparer;
var result = customers.OrderBy(customer => customer, customerComparer).FirstOrDefault();
Если я запускаю программу, я получаю следующий результат:
Compare 4 - 2 => 1
Compare 4 - 9 => -1
Compare 4 - 5 => -1
Compare 4 - 8 => -1
Compare 4 - 3 => 1
Compare 4 - 6 => -1
Compare 4 - 7 => -1
Compare 4 - 4 => 0
Compare 4 - 1 => 1
Compare 4 - 6 => -1
Compare 4 - 1 => 1
Compare 3 - 2 => 1
Compare 3 - 3 => 0
Compare 3 - 1 => 1
Compare 3 - 4 => -1
Compare 3 - 4 => -1
Compare 3 - 1 => 1
Compare 2 - 2 => 0
Compare 2 - 1 => 1
Compare 4 - 4 => 0
Compare 4 - 3 => 1
Compare 9 - 6 => 1
Compare 9 - 7 => 1
Compare 9 - 9 => 0
Compare 9 - 5 => 1
Compare 9 - 8 => 1
Compare 9 - 9 => 0
Compare 9 - 8 => 1
Compare 7 - 6 => 1
Compare 7 - 7 => 0
Compare 7 - 8 => -1
Compare 7 - 5 => 1
Compare 6 - 6 => 0
Compare 6 - 5 => 1
Compare 7 - 7 => 0
Compare 7 - 8 => -1
Compare 7 - 7 => 0
Я вижу некоторые странные вещи:
- Заказчик [4] сравнивается сам с собой несколько раз. Это также для клиентов [7] и [6], но не для клиентов [8] и [1].
- Клиент [4] сравнивается с Клиентом [6], и, спустя несколько сравнений, Клиент [4] снова сравнивается с Клиентом [6].
- Клиенты [3] и [4] сравниваются дважды без каких-либо других сравнений между ними.
- Двойное сравнение также для клиентов [4] и [1], а немного позже для [4] и [3], но не для других клиентов.
Почему это будет эффективный алгоритм сортировки?
1 ответ
Как упоминал Йерун Мостерт, вероятно, он сравнивает элементы с самими собой, чтобы упростить алгоритм, а простота в некоторых случаях может улучшить производительность. Я ожидал, что алгоритмы сортировки будут достаточно хорошо оптимизированы, поэтому я не буду беспокоиться о нескольких дополнительных сравнениях. Также обратите внимание, что Orderby
гарантированно стабильна, и это может накладывать дополнительные ограничения на алгоритм.
Чтобы решить проблему возврата наибольшего значения, я бы предложил создать вашу собственную реализацию, которая выполняет итерацию по коллекции и возвращает наименьшее / наибольшее. Это довольно тривиально. Или используйте что-то вроде MoreLinq MaxBy / MinBy.
Я слышал, как кто-то сказал, что если вы используете OrderBy и берете только первый элемент, то упорядочивается не вся последовательность.
Внутренняя работа OrderBy
не задокументирована. Теоретически среда выполнения может проверять всю последовательность вызовов linq и создавать оптимальный код. Изменить:
- В .Net core 3.x и более поздних версиях это, похоже, оптимизируется до
O(n)
(спасибо Мэтью Уотсону за указание на это). - В структуре .Net похоже, что он создаст EnumerableSorter, который в конечном итоге выполнит быструю сортировку всего, предположительно стабильного варианта. т.е.
O(n log n)
- В структуре сущностей запрос должен быть переведен на SQL и запущен через оптимизатор запросов, что, вероятно, приведет к
O(n)
(или лучше) времени выполнения.
Похожие вопросы
Новые вопросы
c#
C # (произносится как «резкий») - это высокоуровневый, статически типизированный язык программирования с несколькими парадигмами, разработанный Microsoft. Код C # обычно нацелен на семейство инструментов и сред выполнения Microsoft .NET, включая, среди прочего, .NET Framework, .NET Core и Xamarin. Используйте этот тег для вопросов о коде, написанном на C # или в формальной спецификации C #.