web-dev-qa-db-ja.com

C#の並べ替えと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);
    }
}
95
user215675

なぜ測定しないのですか:

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は高速です

88
Darin Dimitrov

いいえ、それらは同じアルゴリズムではありません。まず、LINQ OrderBystableとして文書化されています(つまり、2つのアイテムのNameが同じ場合、それらは ' llは元の順序で表示されます)。

また、クエリをバッファリングするか、数回反復するかによっても異なります(結果をバッファリングしない限り、foreachごとにLINQ-to-Objectsが並べ替えられます)。

OrderByクエリの場合、次のものも使用したいと思います。

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

(ために {yourchoice}CurrentCultureOrdinal、またはInvariantCultureのいずれか)。

List<T>.Sort

このメソッドは、QuickSortアルゴリズムを使用するArray.Sortを使用します。この実装は、不安定なソートを実行します。つまり、2つの要素が等しい場合、それらの順序は保持されない可能性があります。対照的に、安定したソートでは、等しい要素の順序が保持されます。

Enumerable.OrderBy

このメソッドは安定したソートを実行します。つまり、2つの要素のキーが等しい場合、要素の順序は保持されます。対照的に、不安定なソートでは、同じキーを持つ要素の順序は保持されません。ソート;つまり、2つの要素が等しい場合、それらの順序は保持されない可能性があります。対照的に、安定したソートでは、等しい要素の順序が保持されます。

118
Marc Gravell

Darin Dimitrovの答えは、すでにソートされた入力に直面したとき、OrderByList.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();
    }
}
55
phoog

SortOrderByの別の違いに注意することが重要だと思います。

時間がかかる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を呼び出す場合があります 2 n log(n 回まで、ソートアルゴリズムの成功に応じて.

35
Omer Raviv

手短に :

リスト/配列ソート():

  • 不安定なソート。
  • インプレースで完了。
  • Introsort/Quicksortを使用します。
  • カスタム比較は、比較器を提供することにより行われます。比較にコストがかかる場合、OrderBy()(キーを使用できるようにする、以下を参照)よりも遅くなる可能性があります。

OrderBy/ThenBy():

  • 安定したソート。
  • インプレースではありません。
  • Quicksortを使用します。クイックソートは安定したソートではありません。ここに秘isがあります:ソート時に、2つの要素のキーが等しい場合、初期順序(ソート前に保存されている)を比較します。
  • キーを使用して(ラムダを使用して)値の要素を並べ替えることができます(例:x => x.Id)。ソートする前に、すべてのキーが最初に抽出されます。これにより、Sort()およびカスタム比較演算子を使用するよりもパフォーマンスが向上する場合があります。

ソース: [〜#〜] mdsn [〜#〜]参照ソース および dotnet/coreclr リポジトリ(GitHub)。

上記のステートメントの一部は、現在の.NETフレームワークの実装(4.7.2)に基づいています。将来変更される可能性があります。

10
tigrou

メソッドOrderByおよびSortで使用されるアルゴリズムの複雑さを計算する必要があります。私が覚えているように、QuickSortにはn(log n)の複雑さがあります。ここで、nは配列の長さです。

私もorderbyを検索しましたが、msdnライブラリでも情報を見つけることができませんでした。 1つのプロパティのみに関連する同じ値と並べ替えがない場合、Sort()メソッドを使用することを好みます。そうでない場合は、OrderByを使用します。

0
icaptan