C# сортировка и сравнение по порядку



Я могу сортировать список с помощью сортировки или 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);
}
}
896   7  

7 ответов:

почему бы не измерить:

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();
    }
}

на моем компьютере при компиляции в режиме релиза эта программа печатает:

Sort: 1162ms
OrderBy: 1269ms

обновление:

как предложил @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 работает лучше.


обновление 2:

и с помощью случайных имена:

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 быстрее

нет, это не один и тот же алгоритм. Для начала, LINQ OrderBy описана как стабильный (т. е. если два элемента имеют одинаковую Name, они появятся в исходном порядке).

это также зависит от того, какой буфер запроса против его итерации несколько раз (по LINQ-к-объектов, если вы буферов результате, будет повторно заказать за foreach).

на OrderBy запрос, я бы также хотел использовать:

OrderBy(n => n.Name, StringComparer.{yourchoice}IgnoreCase);

(для {yourchoice} один из CurrentCulture,Ordinal или InvariantCulture).

List<T>.Sort

этот метод использует массив.Сорт, который использует алгоритм быстрой сортировки. Этот реализации осуществляется нестрогая рода; то есть, если два элемента равны, их порядок может не быть консервированный. Напротив, стабильная сортировка сохраняет порядок элементов, которые они равны.

Enumerable.OrderBy

этот метод выполняет стабильная сортировка, то есть, если ключи от двух элементов равны, порядок элементов сохраняется. Напротив, нестабильная сортировка не сохраняет порядок элементов, имеющих один и тот же ключ. рода; то есть, если два элемента равны, их порядок может не быть консервированный. Напротив, стабильная сортировка сохраняет порядок элементов, которые они равны.

ответ Дарина Димитрова показывает, что 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();
    }
}

Я думаю, это важно отметить еще одно различие между 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 раз, в то время как можно назвать CalculateSalary до 2n log (n) раз, в зависимости от успеха алгоритма сортировки.

вы должны рассчитать сложность алгоритмов, используемых методами OrderBy и Sort. QuickSort имеет сложность n (log n), как я помню, где n-длина массива.

Я тоже искал заказатьпо, но я не смог найти никакой информации даже в библиотеке MSDN. если у вас нет одинаковых значений и сортировки, связанных только с одним свойством, я предпочитаю использовать Метод Sort (); если нет, то используйте OrderBy.

Я просто хочу добавить, что orderby является более полезным.

почему?

потому что я могу сделать это

        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)

в двух словах :

Сортировка Списка / Массива ():

  • нестрогая сортировка.
  • сделать на месте.
  • Используйте Introsort / Quicksort.
  • Пользовательское сравнение выполняется путем предоставления компаратора. Если сравнение дорого, оно может быть медленнее, чем OrderBy() (которые позволяют использовать ключи, см. ниже).

OrderBy/ThenBy ():

  • стабильная сортировка.
  • не на месте.
  • Использование Быстрой Сортировки. Быстрая сортировка не является стабильной сортировки. Вот трюк: при сортировке, если два элемента имеют одинаковый ключ, он сравнивает их начальный порядок (который был сохранен до сортировки).
  • позволяет использовать ключи (используя лямбды) для сортировки элементов по их значениям (например:x => x.Id). Все ключи извлекаются сначала перед сортировкой. Это может привести к повышению производительности по сравнению с использованием Sort() и пользовательского компаратора.

источники: MDSN,источник и dotnet / coreclr репозиторий (GitHub).

некоторые из перечисленных выше операторов основаны на текущей реализации .NET framework (4.7.2). Это может измениться в будущем.

Comments

    Ничего не найдено.