Je peux trier une liste à l'aide de Trier ou OrderBy. Lequel est le plus rapide? Est-ce que les deux travaillent sur le même algorithme?
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);
}
}
Pourquoi ne pas le mesurer:
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();
}
}
Sur mon ordinateur, une fois compilé en mode Release, ce programme imprime:
Sort: 1162ms
OrderBy: 1269ms
MISE À JOUR:
Comme suggéré par @Stefan, voici les résultats du tri d'une grande liste moins de fois:
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);
Impressions:
Sort: 8965ms
OrderBy: 8460ms
Dans ce scénario, OrderBy semble mieux performer.
UPDATE2:
Et en utilisant des noms aléatoires:
List<Person> persons = new List<Person>();
for (int i = 0; i < 100000; i++)
{
persons.Add(new Person("P" + i.ToString(), RandomString(5, true)));
}
Où:
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();
}
Rendements:
Sort: 8968ms
OrderBy: 8728ms
Toujours OrderBy est plus rapide
Non, ils ne sont pas le même algorithme. Pour commencer, le LINQ OrderBy
est documenté comme stable (c.-à-d. Si deux éléments ont le même Name
, ils ' apparaissent dans leur ordre d'origine).
Cela dépend également de la mise en mémoire tampon de la requête ou de sa répétition plusieurs fois (LINQ-to-Objects, à moins que le résultat ne soit mis en mémoire tampon, sera réorganisé par foreach
).
Pour la requête OrderBy
, je serais également tenté d’utiliser:
OrderBy(n => n.Name, StringComparer.{yourchoice}IgnoreCase);
(pour {yourchoice}
l'un des CurrentCulture
, Ordinal
ou InvariantCulture
).
Cette méthode utilise Array.Sort, qui utilise l'algorithme QuickSort. Cette implémentation effectue un tri instable; c'est-à-dire que si deux éléments sont égaux, leur ordre pourrait ne pas être conservé. En revanche, un tri stable préserve l’ordre des éléments égaux.
Cette méthode effectue un tri stable; en d'autres termes, si les clés de deux éléments sont égales, l'ordre des éléments est conservé. En revanche, un tri instable ne conserve pas l'ordre des éléments ayant la même clé. Trier; c'est-à-dire que si deux éléments sont égaux, leur ordre pourrait ne pas être conservé. En revanche, un tri stable préserve l’ordre des éléments égaux.
La réponse de Darin Dimitrov montre que OrderBy
est légèrement plus rapide que List.Sort
lorsque vous faites face à une entrée déjà triée. J'ai modifié son code afin qu'il trie à plusieurs reprises les données non triées, et OrderBy
est dans la plupart des cas légèrement plus lent.
De plus, le test OrderBy
utilise ToArray
pour forcer l'énumération de l'énumérateur Linq, mais renvoie évidemment un type (Person[]
) qui est différent du type d’entrée (List<Person>
). J'ai donc relancé le test en utilisant ToList
plutôt que ToArray
et j'ai une différence encore plus grande:
Sort: 25175ms
OrderBy: 30259ms
OrderByWithToList: 31458ms
Le 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();
}
}
Je pense qu'il est important de noter une autre différence entre Sort
et OrderBy
:
Supposons qu'il existe une méthode Person.CalculateSalary()
, qui prend beaucoup de temps; peut-être plus que l'opération même de trier une grande liste.
Comparer
// Option 1
persons.Sort((p1, p2) => Compare(p1.CalculateSalary(), p2.CalculateSalary()));
// Option 2
var query = persons.OrderBy(p => p.CalculateSalary());
Option 2 peut offrir des performances supérieures, car elle n'appelle que la méthode CalculateSalary
n, alors que l'option Sort
peut appelez CalculateSalary
jusqu'à 2 n log ( n) fois, en fonction du succès de l'algorithme de tri.
En un mot :
Trier par liste/tableau ():
OrderBy/ThenBy ():
x => x.Id
). Toutes les clés sont extraites avant le tri. Cela pourrait entraîner de meilleures performances que l'utilisation de Sort () et d'un comparateur personnalisé.Sources: MDSN , source de référence et dotnet/coreclr référentiel (GitHub).
Certaines des déclarations répertoriées ci-dessus sont basées sur la mise en œuvre actuelle du framework .NET (4.7.2). Cela pourrait changer dans le futur.
vous devez calculer la complexité des algorithmes utilisés par les méthodes OrderBy et Sort. QuickSort a une complexité de n (log n), comme je m'en souviens, où n est la longueur du tableau.
j'ai aussi cherché orderby's, mais je n'ai trouvé aucune information, même dans la bibliothèque msdn. si vous n'avez pas les mêmes valeurs et le tri lié à une seule propriété, je préfère utiliser la méthode Sort (); sinon utilisez OrderBy.