it-swarm.com.de

C # Sort und OrderBy Vergleich

Ich kann eine Liste mit Sort oder OrderBy sortieren. Welches ist schneller? Arbeiten beide an demselben Algorithmus?

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);
    }
}
95
user215675

Warum nicht messen:

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

Auf meinem Computer wird beim Kompilieren im Release-Modus folgendes Programm gedruckt:

Sort: 1162ms
OrderBy: 1269ms

AKTUALISIEREN:

Wie von @Stefan vorgeschlagen, sind hier die Ergebnisse des selteneren Sortierens einer großen Liste:

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

Drucke:

Sort: 8965ms
OrderBy: 8460ms

In diesem Szenario sieht es so aus, als würde OrderBy eine bessere Leistung erbringen.


UPDATE2:

Und mit zufälligen Namen:

List<Person> persons = new List<Person>();
for (int i = 0; i < 100000; i++)
{
    persons.Add(new Person("P" + i.ToString(), RandomString(5, true)));
}

Woher:

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

Erträge:

Sort: 8968ms
OrderBy: 8728ms

Dennoch ist OrderBy schneller

88
Darin Dimitrov

Nein, sie sind nicht der gleiche Algorithmus. Für den Anfang wird der LINQ OrderBy als stabil dokumentiert (dh wenn zwei Elemente den gleichen Name haben, haben sie ' erscheint in der ursprünglichen Reihenfolge).

Es hängt auch davon ab, ob Sie die Abfrage puffern oder mehrmals iterieren (LINQ-to-Objects wird, sofern Sie das Ergebnis nicht puffern, nach foreach neu sortiert).

Für die Abfrage OrderBy wäre ich auch versucht, Folgendes zu verwenden:

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

(zum {yourchoice} eines von CurrentCulture, Ordinal oder InvariantCulture).

List<T>.Sort

Diese Methode verwendet Array.Sort, das den QuickSort-Algorithmus verwendet. Diese Implementierung führt eine instabile Sortierung durch. Das heißt, wenn zwei Elemente gleich sind, wird ihre Reihenfolge möglicherweise nicht beibehalten. Im Gegensatz dazu behält eine stabile Sortierung die Reihenfolge der Elemente bei, die gleich sind.

Enumerable.OrderBy

Diese Methode führt eine stabile Sortierung durch. Das heißt, wenn die Schlüssel zweier Elemente gleich sind, bleibt die Reihenfolge der Elemente erhalten. Im Gegensatz dazu behält eine instabile Sortierung die Reihenfolge von Elementen mit demselben Schlüssel nicht bei. Sortieren; Das heißt, wenn zwei Elemente gleich sind, wird ihre Reihenfolge möglicherweise nicht beibehalten. Im Gegensatz dazu behält eine stabile Sortierung die Reihenfolge der Elemente bei, die gleich sind.

118
Marc Gravell

Darin Dimitrovs Antwort zeigt, dass OrderBy etwas schneller ist als List.Sort bei bereits sortierten Eingaben. Ich habe seinen Code so geändert, dass er die unsortierten Daten wiederholt sortiert, und OrderBy ist in den meisten Fällen etwas langsamer.

Darüber hinaus verwendet der Test OrderByToArray, um die Aufzählung des Linq-Enumerators zu erzwingen, aber dies gibt offensichtlich einen Typ zurück (Person[]) unterscheidet sich vom Eingabetyp (List<Person>). Ich habe daher den Test mit ToList und nicht mit ToArray wiederholt und einen noch größeren Unterschied festgestellt:

Sort: 25175ms
OrderBy: 30259ms
OrderByWithToList: 31458ms

Der Code:

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

Ich denke, es ist wichtig, einen weiteren Unterschied zwischen Sort und OrderBy zu beachten:

Angenommen, es gibt eine Person.CalculateSalary() -Methode, die viel Zeit in Anspruch nimmt. möglicherweise mehr als nur das Sortieren einer großen Liste.

Vergleichen

// Option 1
persons.Sort((p1, p2) => Compare(p1.CalculateSalary(), p2.CalculateSalary()));
// Option 2
var query = persons.OrderBy(p => p.CalculateSalary()); 

Option 2 hat möglicherweise eine überlegene Leistung, da nur die Methode CalculateSalary aufgerufen wird n, wohingegen die Option Sort Rufe CalculateSalary bis zu 2 n log ( n mal auf, abhängig vom Erfolg des Sortieralgorithmus.

35
Omer Raviv

In einer Nussschale :

Listen-/Array-Sortierung ():

  • Instabile Sortierung.
  • Vor Ort erledigt.
  • Verwenden Sie Introsort/Quicksort.
  • Der benutzerdefinierte Vergleich erfolgt durch Bereitstellung eines Vergleichers. Wenn der Vergleich teuer ist, ist er möglicherweise langsamer als OrderBy () (die die Verwendung von Schlüsseln ermöglichen, siehe unten).

OrderBy/ThenBy ():

  • Stabile Sorte.
  • Nicht an Ort und Stelle.
  • Verwenden Sie Quicksort. Quicksort ist keine stabile Sorte. Hier ist der Trick: Wenn beim Sortieren zwei Elemente den gleichen Schlüssel haben, wird ihre ursprüngliche Reihenfolge verglichen (die vor dem Sortieren gespeichert wurde).
  • Ermöglicht die Verwendung von Schlüsseln (unter Verwendung von Lambdas), um Elemente nach ihren Werten zu sortieren (zB: x => x.Id). Alle Schlüssel werden vor dem Sortieren zuerst extrahiert. Dies kann zu einer besseren Leistung führen als die Verwendung von Sort () und eines benutzerdefinierten Vergleichers.

Quellen: MDSN , Referenzquelle und dotnet/coreclr Repository (GitHub).

Einige der oben aufgeführten Anweisungen basieren auf der aktuellen .NET Framework-Implementierung (4.7.2). Es könnte sich in Zukunft ändern.

10
tigrou

sie sollten die Komplexität der von den Methoden OrderBy und Sort verwendeten Algorithmen berechnen. QuickSort hat, wie ich mich erinnere, eine Komplexität von n (log n), wobei n die Länge des Arrays ist.

ich habe auch nach orderby's gesucht, aber ich konnte keine Informationen selbst in der MSDN-Bibliothek finden. Wenn Sie nicht dieselben Werte und Sortierungen für nur eine Eigenschaft haben, bevorzuge ich die Methode Sort (). Wenn nicht, verwenden Sie OrderBy.

0
icaptan