2016-01-08T23:52:04の更新内容

programming/netfx/sorting/0_basictypes/index.wiki.txt

current previous
4,31 4,9
 

        

        
 
#navi(..)
#navi(..)
 

        

        
~
ここでは、配列・List・Dictionaryなどの各種コレクションを使ったデータの並べ替え、ソート(sort)について解説します。 ソートするにはどのようにすればよいかを主眼に置くため、ここでは数値型や文字列型などの単純型(基本型)からなるコレクションのソートのみを扱います。 ソートしたい対象が決まっていて、そのソート方法を知りたい場合は以下の項を参照してください。
ここでは配列・List・Dictionaryなどの各種コレクションを使ったデータの並べ替え、コレクションのソート方法について見ていきます。 なお、まずは数値・文字列等の単純型(基本型)を格納する配列・コレクションのソートについてのみ説明します。 基本型のデフォルトのソート順については[[programming/netfx/sorting/0_basictypes_1_defaultsortorder]]、複合型を格納している配列・コレクションのソートについては[[programming/netfx/sorting/1_compositetypes]]、ジャグ配列・多次元配列のソートについては[[programming/netfx/sorting/2_arrays]]で順次解説します。
 

        

        
~
:配列をソートしたい|→[[#Array.Sort]]
#relevantdocs
+
:Listをソートしたい|→[[#List.Sort]]
+
:Dictionaryをソートしたい|→[[#SortDictionary]]
+
:降順でソートしたい&br;ソート順を逆にしたい|→[[#SortDescending]]
+

          
+
上記のようなソートのメソッドについては既に知っていて、その上でソートの動作を詳細にカスタマイズしたり、基本型以外の型をソートしたいといった場合には、ソート対象やソートの目的に応じて以下の文章を参照してください。
+

          
+
#relevantdocs(目的別リンク)
+

          
+
:独自に定義した構造体やクラスをソートしたい&br;独自に定義した順序でソートしたい|→[[programming/netfx/sorting/1_compositetypes#SortCompositeType]]、[[programming/netfx/comparison/0_comparison]]
+
:複数のキーを指定してソートしたい|→[[programming/netfx/sorting/1_compositetypes#SortWithMultipleKeys]]
+
:ジャグ配列・多次元配列をソートしたい|→[[programming/netfx/sorting/2_arrays]]
+
:数値にNaNや±∞が含まれている場合のソート結果を知りたい|→[[programming/netfx/sorting/0_basictypes_1_defaultsortorder#DefaultSortOrder_Numerics]]
+
:列挙体(enum)を値順/名前順でソートしたい|→[[programming/netfx/sorting/0_basictypes_1_defaultsortorder#DefaultSortOrder_Enum]]
+
:日付型(DateTime・DateTimeOffset)をソートしたい&br;UTCとローカル時刻が混在する場合のソート結果を知りたい|[[programming/netfx/sorting/0_basictypes_1_defaultsortorder#DefaultSortOrder_DateTime]]|
+
:大文字小文字の違いを考慮して/無視してソートしたい|→[[programming/netfx/sorting/0_basictypes_1_defaultsortorder#DefaultSortOrder_String_IgnoreCase]]、[[programming/netfx/string/2_2_compareoptions#StringComparisonStringComparer]]
+
:ひらがな/カタカナ、全角/半角、アクセント記号の有無などの文字種の違いを考慮して/無視してソートしたい|→[[programming/netfx/string/2_2_compareoptions#CompareOptions]]
+
:ソートのメソッドで使用されるインターフェイスやデリゲートについて知りたい|→[[programming/netfx/comparison/0_comparison]]、[[programming/netfx/sorting/9_generic_sort_algorithm]]
+
:独自のソートアルゴリズムを実装したい&br;ソート処理と各種インターフェイス・デリゲートの関係を知りたい|→[[programming/netfx/sorting/9_generic_sort_algorithm]]
+

          
+
#relevantdocs-end
+

          
+
#relevantdocs(その他関連するページ)
 

        

        
 
-[[programming/netfx/arrays]]
-[[programming/netfx/arrays]]
 
--[[programming/netfx/arrays/2_operations]]
--[[programming/netfx/arrays/2_operations]]
52,7 30,7
 
ここでは、配列・List・Dictionaryなどを使ったデータの並べ替えについて見ていきます。 まずは昇順(小さい順)でソートする方法について解説します。 [[降順(大きい順)でのソート方法>#SortDescending]]については追って解説します。
ここでは、配列・List・Dictionaryなどを使ったデータの並べ替えについて見ていきます。 まずは昇順(小さい順)でソートする方法について解説します。 [[降順(大きい順)でのソート方法>#SortDescending]]については追って解説します。
 

        

        
 
**配列のソート (Array.Sort) [#Array.Sort]
**配列のソート (Array.Sort) [#Array.Sort]
~
配列をソートするには、&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};を使います。 このメソッドは静的メソッドで、引数に指定された配列をソートします。
配列をソートするには、&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};を使います。 このメソッドは静的メソッドです。 ソートされた配列が作成され戻り値として返されるのではなく、引数に指定された配列自体をソートします。
 

        

        
 
#tabpage(codelang=cs,container-title=数値のソート)
#tabpage(codelang=cs,container-title=数値のソート)
 
#code{{
#code{{
67,7 45,6
 
    // ソート
    // ソート
 
    Array.Sort(arr);
    Array.Sort(arr);
 

        

        
+
    // ソート結果を表示
 
    foreach (int val in arr) {
    foreach (int val in arr) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
 
    }
    }
87,7 64,6
 
    // ソート
    // ソート
 
    Array.Sort(arr);
    Array.Sort(arr);
 

        

        
+
    // ソート結果を表示
 
    foreach (string val in arr) {
    foreach (string val in arr) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
 
    }
    }
107,7 83,6
 
    ' ソート
    ' ソート
 
    Array.Sort(arr)
    Array.Sort(arr)
 

        

        
+
    ' ソート結果の表示
 
    For Each val As Integer In arr
    For Each val As Integer In arr
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
126,7 101,6
 
    ' ソート
    ' ソート
 
    Array.Sort(arr)
    Array.Sort(arr)
 

        

        
+
    ' ソート結果を表示
 
    For Each val As String In arr
    For Each val As String In arr
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
144,92 118,12
 
a, aa, ab, abc, acb, b, 
a, aa, ab, abc, acb, b, 
 
}}
}}
 

        

        
+
Array.Sortメソッドでは、''引数に指定された配列自体をソートします''(破壊的変更)。 ソートされた配列が新たに作成され戻り値として返されることはありません。 そのため、ソート前の状態も維持しておきたい場合は、[[Array.Cloneメソッド>programming/netfx/arrays/2_operations#Array.Clone]]などを使ってあらかじめ配列の複製を作っておき、その後で変更用の配列をソートする必要があります。
+

          
+
#remarks
+
非破壊的なソートを行いたい場合は[[Enumerable.OrderByメソッド>#Enumerable.OrderBy]]を使うことができます。
+
#remarks-end
+

          
 
#remarks
#remarks
 
ジャグ配列・多次元配列のソートについては[[programming/netfx/sorting/2_arrays]]で解説します。
ジャグ配列・多次元配列のソートについては[[programming/netfx/sorting/2_arrays]]で解説します。
 
#remarks-end
#remarks-end
 

        

        
+
独自に定義した型をソートしようとする場合など、ソートができない場合は例外&msdn(netfx,type,System.InvalidOperationException){InvalidOperationException};がスローされます。
+

          
+
#tabpage(codelang=cs,container-title=Array.Sortでソートできない例)
+
#code{{
+
using System;
+

          
+
// 独自に定義した型
+
class Account {
+
  public string Name;
+

          
+
  public Account(string name)
+
  {
+
    this.Name = name;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // ソート対象の配列
+
    Account[] arr = new Account[] {new Account("Bob"), new Account("Alice"), new Account("Charlie")};
+

          
+
    // ソート
+
    Array.Sort(arr);
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 独自に定義した型
+
Class Account
+
  Public Name As String
+

          
+
  Public Sub New(ByVal name As String)
+
    Me.Name = name
+
  ENd Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    ' ソート対象の配列
+
    Dim arr() As Account = New Account() {New Account("Bob"), New Account("Alice"), New Account("Charlie")}
+

          
+
    ' ソート
+
    Array.Sort(arr)
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
System.InvalidOperationException: 配列にある 2 つの要素を比較できませんでした。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。
+
   場所 System.Collections.Comparer.Compare(Object a, Object b)
+
   場所 System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
+
   場所 System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
+
   場所 System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer)
+
   場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
+
   --- 内部例外スタック トレースの終わり ---
+
   場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
+
   場所 System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
+
   場所 System.Array.Sort[T](T[] array)
+
   場所 Sample.Main()
+
}}
+

          
+
これはソート対象の大小関係が定義されていないために発生します。 ソートを行うためにはソート対象の大小関係が比較できなければなりませんが、上記の例ではAccountクラスの大小関係がどこにも定義されておらず比較ができないため、ソートの際に例外が発生しています。
+

          
+
Array.Sortメソッドでソートを行うには、ソート対象の型に&msdn(netfx,type,System.IComparable`1){IComparable<T>};(または&msdn(netfx,type,System.IComparable){IComparable};)インターフェイスを実装するか、あるいはSortメソッドの引数に&msdn(netfx,type,System.Collections.Generic.IComparer`1){IComparer<T>};(または&msdn(netfx,type,System.Collections.IComparer){IComparer};)インターフェイス、もしくは&msdn(netfx,type,System.Comparison`1){Comparison<T>};デリゲートを指定する必要があります。
+

          
+
#remarks
+
詳しくは[[programming/netfx/sorting/1_compositetypes]]を参照してください。 また個々のインターフェイスとその実装方法については[[programming/netfx/comparison/0_comparison]]を参照してください。
+
#remarks-end
+

          
 
**Listのソート (List.Sort) [#List.Sort]
**Listのソート (List.Sort) [#List.Sort]
~
List<T>をソートするには、&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};を使います。 List.Sortメソッドはメソッドを呼び出したインスタンス自身をソートします。
List<T>をソートするには、&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};を使います。 Array.Sortメソッドとは異なりこのメソッドはインスタンスメソッドですが、動作はArray.Sortメソッドと同様にSortメソッドを呼び出したインスタンス自体がソートされます。 戻り値は無いため、ソートされたList<T>が作成されて戻り値として返されることはありません。
 

        

        
 
#tabpage(codelang=cs,container-title=数値のソート)
#tabpage(codelang=cs,container-title=数値のソート)
 
#code{{
#code{{
245,7 139,6
 
    // ソート
    // ソート
 
    list.Sort();
    list.Sort();
 

        

        
+
    // ソート結果を表示
 
    foreach (int val in list) {
    foreach (int val in list) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
 
    }
    }
266,7 159,6
 
    // ソート
    // ソート
 
    list.Sort();
    list.Sort();
 

        

        
+
    // ソート結果を表示
 
    foreach (string val in list) {
    foreach (string val in list) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
 
    }
    }
287,7 179,6
 
    ' ソート
    ' ソート
 
    list.Sort()
    list.Sort()
 

        

        
+
    ' ソート結果を表示
 
    For Each val As Integer In list
    For Each val As Integer In list
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
307,7 198,6
 
    ' ソート
    ' ソート
 
    list.Sort()
    list.Sort()
 

        

        
+
    ' ソート結果を表示
 
    For Each val As String In list
    For Each val As String In list
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
325,88 215,8
 
a, aa, ab, abc, acb, b, 
a, aa, ab, abc, acb, b, 
 
}}
}}
 

        

        
+
List.Sortメソッドでは、''インスタンス自身をソートします''(破壊的変更)。 ソートされたListが新たに作成され戻り値として返されることはありません。 そのため、ソート前の状態も維持しておきたい場合は、あらかじめListの複製を作っておき([[programming/netfx/collections/2_generic_1_list#List_Cloning]])、その後で変更用のListをソートする必要があります。
+

          
+
#remarks
+
非破壊的なソートを行いたい場合は[[Enumerable.OrderByメソッド>#Enumerable.OrderBy]]を使うことができます。
+
#remarks-end
+

          
+
独自に定義した型をソートしようとする場合など、ソートができない場合は例外&msdn(netfx,type,System.InvalidOperationException){InvalidOperationException};がスローされます。
+

          
+
#tabpage(codelang=cs,container-title=List.Sortでソートできない例)
+
#code{{
+
using System;
+

          
+
// 独自に定義した型
+
class Account {
+
  public string Name;
+

          
+
  public Account(string name)
+
  {
+
    this.Name = name;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // ソート対象のList
+
    List<Account> list = new List<Account>(new Account[] {new Account("Bob"), new Account("Alice"), new Account("Charlie")});
+

          
+
    // ソート
+
    list.Sort();
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
' 独自に定義した型
+
Class Account
+
  Public Name As String
+

          
+
  Public Sub New(ByVal name As String)
+
    Me.Name = name
+
  ENd Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    ' ソート対象のList
+
    Dim list As New List(Of Account)(New Account() {New Account("Bob"), New Account("Alice"), New Account("Charlie")})
+

          
+
    ' ソート
+
    list.Sort()
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
System.InvalidOperationException: 配列にある 2 つの要素を比較できませんでした。 ---> System.ArgumentException: 少なくとも 1 つのオブジェクトで IComparable を実装しなければなりません。
+
   場所 System.Collections.Comparer.Compare(Object a, Object b)
+
   場所 System.Collections.Generic.ObjectComparer`1.Compare(T x, T y)
+
   場所 System.Collections.Generic.ArraySortHelper`1.SwapIfGreaterWithItems(T[] keys, IComparer`1 comparer, Int32 a, Int32 b)
+
   場所 System.Collections.Generic.ArraySortHelper`1.QuickSort(T[] keys, Int32 left, Int32 right, IComparer`1 comparer)
+
   場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
+
   --- 内部例外スタック トレースの終わり ---
+
   場所 System.Collections.Generic.ArraySortHelper`1.Sort(T[] keys, Int32 index, Int32 length, IComparer`1 comparer)
+
   場所 System.Array.Sort[T](T[] array, Int32 index, Int32 length, IComparer`1 comparer)
+
   場所 System.Collections.Generic.List`1.Sort(Int32 index, Int32 count, IComparer`1 comparer)
+
   場所 Sample.Main()
+
}}
+

          
+
これはソート対象の大小関係が定義されていないために発生します。 ソートを行うためにはソート対象の大小関係が比較できなければなりませんが、上記の例ではAccountクラスの大小関係がどこにも定義されておらず比較ができないため、ソートの際に例外が発生しています。
+

          
+
List.Sortメソッドでソートを行うには、ソート対象の型に&msdn(netfx,type,System.IComparable`1){IComparable<T>};(または&msdn(netfx,type,System.IComparable){IComparable};)インターフェイスを実装するか、あるいはSortメソッドの引数に&msdn(netfx,type,System.Collections.Generic.IComparer`1){IComparer<T>};(または&msdn(netfx,type,System.Collections.IComparer){IComparer};)インターフェイス、もしくは&msdn(netfx,type,System.Comparison`1){Comparison<T>};デリゲートを指定する必要があります。
+

          
+
#remarks
+
詳しくは[[programming/netfx/sorting/1_compositetypes]]を参照してください。 また個々のインターフェイスとその実装方法については[[programming/netfx/comparison/0_comparison]]を参照してください。
+
#remarks-end
+

          
 
**ArrayListのソート (ArrayList.Sort) [#ArrayList.Sort]
**ArrayListのソート (ArrayList.Sort) [#ArrayList.Sort]
~
ArrayListをソートするには、&msdn(netfx,member,System.Collections.ArrayList.Sort){ArrayList.Sortメソッド};を使います。 使い方・動作と結果は[[List<T>.Sort>#List.Sort]]と同様です。
ArrayListをソートするには、&msdn(netfx,member,System.Collections.ArrayList.Sort){ArrayList.Sortメソッド};を使います。 使い方・動作と結果はList<T>.Sortと同様です。
 

        

        
 
#tabpage(codelang=cs,container-title=数値のソート)
#tabpage(codelang=cs,container-title=数値のソート)
 
#code{{
#code{{
422,7 232,6
 
    // ソート
    // ソート
 
    list.Sort();
    list.Sort();
 

        

        
+
    // ソート結果を表示
 
    foreach (int val in list) {
    foreach (int val in list) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
 
    }
    }
443,7 252,6
 
    // ソート
    // ソート
 
    list.Sort();
    list.Sort();
 

        

        
+
    // ソート結果を表示
 
    foreach (string val in list) {
    foreach (string val in list) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
 
    }
    }
464,7 272,6
 
    ' ソート
    ' ソート
 
    list.Sort()
    list.Sort()
 

        

        
+
    ' ソート結果を表示
 
    For Each val As Integer In list
    For Each val As Integer In list
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
484,7 291,6
 
    ' ソート
    ' ソート
 
    list.Sort()
    list.Sort()
 

        

        
+
    ' ソート結果を表示
 
    For Each val As String In list
    For Each val As String In list
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
524,7 330,6
 
    list.Add(1, "Dave");
    list.Add(1, "Dave");
 
    list.Add(4, "Eve");
    list.Add(4, "Eve");
 

        

        
+
    // SortedListの内容を列挙して表示
 
    foreach (KeyValuePair<int, string> pair in list) {
    foreach (KeyValuePair<int, string> pair in list) {
 
      Console.WriteLine("{0} {1}", pair.Key, pair.Value);
      Console.WriteLine("{0} {1}", pair.Key, pair.Value);
 
    }
    }
547,7 352,6
 
    list.Add("Eve", 4);
    list.Add("Eve", 4);
 
    list.Add("Charlie", 5);
    list.Add("Charlie", 5);
 

        

        
+
    // SortedListの内容を列挙して表示
 
    foreach (KeyValuePair<string, int> pair in list) {
    foreach (KeyValuePair<string, int> pair in list) {
 
      Console.WriteLine("{0} {1}", pair.Key, pair.Value);
      Console.WriteLine("{0} {1}", pair.Key, pair.Value);
 
    }
    }
570,7 374,6
 
    list.Add(1, "Dave")
    list.Add(1, "Dave")
 
    list.Add(4, "Eve")
    list.Add(4, "Eve")
 

        

        
+
    ' SortedListの内容を列挙して表示
 
    For Each pair As KeyValuePair(Of Integer, String) In list
    For Each pair As KeyValuePair(Of Integer, String) In list
 
      Console.WriteLine("{0} {1}", pair.Key, pair.Value)
      Console.WriteLine("{0} {1}", pair.Key, pair.Value)
 
    Next
    Next
592,7 395,6
 
    list.Add("Eve", 4)
    list.Add("Eve", 4)
 
    list.Add("Charlie", 5)
    list.Add("Charlie", 5)
 

        

        
+
    ' SortedListの内容を列挙して表示
 
    For Each pair As KeyValuePair(Of String, Integer) In list
    For Each pair As KeyValuePair(Of String, Integer) In list
 
      Console.WriteLine("{0} {1}", pair.Key, pair.Value)
      Console.WriteLine("{0} {1}", pair.Key, pair.Value)
 
    Next
    Next
624,13 426,9
 
#remarks-end
#remarks-end
 

        

        
 
**Dictionaryのソート [#SortDictionary]
**Dictionaryのソート [#SortDictionary]
~
&msdn(netfx,type,System.Collections.Generic.Dictionary`2){Dictionary<TKey, TValue>};には、[[SortedListとSortedDictionary>#SortedListSortedDictionary]]のように自動的に内容をソートする機能や、[[List.Sort>#List.Sort]]のようにソートを行うメソッドは用意されていません。
&msdn(netfx,type,System.Collections.Generic.Dictionary`2){Dictionary<TKey, TValue>};にはソートを行うメソッドや、SortedListとSortedDictionaryのように自動的にソートする機能はありません。 常にソートされた状態にしておきたい場合はSortedList・SortedDictionaryを選ぶことも出来ますが、要素の追加の度にソートされることによる処理速度の低下を無視できないなどの理由がある場合は、やはりDictionaryを使い、必要な時にソートを行うという方法が必要になります。
 

        

        
~
ここでは、Dictionaryをソートする方法のいくつかを見ていきます。 Dictionary自体をソートすることは出来ないため、以下の方法はいずれも元のDictionaryの状態は変更せず、Dictionaryの内容を参照してソートされた状態で取得する方法(非破壊的なソート)となります。
ここでは、Dictionaryをソートする方法についていくつかのやり方を見ていきます。 なお、Dictionary自体をソートすることは出来ないため、以下のやり方は、いずれも元のDictionaryの状態は変更せずにDictionaryの内容だけをソートされた状態で取得する方法となります。
+

          
+
#remarks
+
常にソートされた状態にしておきたい場合はDictionaryではなく[[SortedList・SortedDictionary>#SortedListSortedDictionary]]を使用することが出来ますが、SortedList・SortedDictionaryでは要素の追加の度にソートされることによるオーバーヘッドが生じます。 これによる処理速度の低下を無視できないなどの理由がある場合は、やはりDictionaryを使い、必要になった時点でソートを行うという方法が必要になります。
+
#remarks-end
 

        

        
 
***KeyValuePairのリストに変換してソートする
***KeyValuePairのリストに変換してソートする
 
1つ目は、Dictionaryの内容を、&msdn(msdn,type,System.Collections.Generic.KeyValuePair`2){KeyValuePair};のリストに変えてからソートする方法です。
1つ目は、Dictionaryの内容を、&msdn(msdn,type,System.Collections.Generic.KeyValuePair`2){KeyValuePair};のリストに変えてからソートする方法です。
662,11 460,7
 
|{"Eve", 4}|
|{"Eve", 4}|
 
#column-end
#column-end
 

        

        
~
KeyValuePairはDictionaryの内部で使われている構造体で、キーと値のペアを一つの構造体として格納するためのものです。 Dictionaryを直接列挙する場合などにも使われます。 DictionaryをこのKeyValuePairのリストに変換してから、&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};を使ってソートします。
KeyValuePairはDictionaryの内部で使われている構造体で、キーと値のペアを一つの構造体として格納するためのものです。 Dictionaryを直接列挙する場合などにも使われます。 DictionaryをこのKeyValuePairのリストに変換してから、&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};を使ってソートします。 ただし、KeyValuePairはそのままではソートできず、List.SortメソッドはInvalidOperationExceptionをスローします。 このため、KeyValuePairのソート方法(比較方法)を定義したメソッドを別途用意し、ソートする際にSortメソッドに渡す必要があります。 Sortメソッドと比較方法の定義(Comparison<T>デリゲート)については、[[programming/netfx/sorting/1_compositetypes]]で改めて詳しく解説します。
+

          
+
ただし、ただ単にKeyValuePairをソートしようとしてもKeyValuePairはそのソート方法が定義されていないため、List.SortメソッドはInvalidOperationExceptionをスローします。 このため、KeyValuePairのソート方法(比較方法)を定義したメソッドを別途用意し、ソートする際にList.Sortメソッドに渡す必要があります。
+

          
+
この方法によるソートの具体例は次のようになります。 この例におけるCompareKeyValuePairメソッドが、KeyValuePairのソート方法(比較方法)を定義するメソッドです。
 

        

        
 
#tabpage(codelang=cs,container-title=List.Sortを使ったDictionaryのソート)
#tabpage(codelang=cs,container-title=List.Sortを使ったDictionaryのソート)
 
#code{{
#code{{
692,11 486,10
 
    dict.Add("Charlie", 5);
    dict.Add("Charlie", 5);
 
    dict.Add("Eve", 4);
    dict.Add("Eve", 4);
 

        

        
~
    // Dictionaryの内容をコピーして、List<KeyValuePair<string, int>>に変換
    // List<KeyValuePair<string, int>>を作成し、Dictionaryの内容をコピー
 
    List<KeyValuePair<string, int>> pairs = new List<KeyValuePair<string, int>>(dict);
    List<KeyValuePair<string, int>> pairs = new List<KeyValuePair<string, int>>(dict);
 

        

        
 
    // List.Sortメソッドを使ってソート
    // List.Sortメソッドを使ってソート
+
    // (引数に比較方法を定義したメソッドを指定する)
 
    pairs.Sort(CompareKeyValuePair);
    pairs.Sort(CompareKeyValuePair);
 

        

        
 
    foreach (KeyValuePair<string, int> pair in pairs) {
    foreach (KeyValuePair<string, int> pair in pairs) {
728,11 521,10
 
    dict.Add("Charlie", 5)
    dict.Add("Charlie", 5)
 
    dict.Add("Eve", 4)
    dict.Add("Eve", 4)
 

        

        
~
    ' Dictionaryの内容をコピーして、List(Of KeyValuePair(Of String, Integer))に変換
    ' List(Of KeyValuePair(Of String, Integer))を作成し、Dictionaryの内容をコピー
 
    Dim pairs As New List(Of KeyValuePair(Of String, Integer))(dict)
    Dim pairs As New List(Of KeyValuePair(Of String, Integer))(dict)
 

        

        
 
    ' List.Sortメソッドを使ってソート
    ' List.Sortメソッドを使ってソート
+
    ' (引数に比較方法を定義したメソッドを指定する)
 
    pairs.Sort(AddressOf CompareKeyValuePair)
    pairs.Sort(AddressOf CompareKeyValuePair)
 

        

        
 
    For Each pair As KeyValuePair(Of String, Integer) In pairs
    For Each pair As KeyValuePair(Of String, Integer) In pairs
751,7 543,7
 
Eve 4
Eve 4
 
}}
}}
 

        

        
~
List.Sortメソッドは、CompareKeyValuePairメソッドで定義されている比較処理にしたがってソートを行います。 上記の例ではキーを比較したため、Dictionaryはキーを基準にソートされました。 比較方法の定義を変えればソートの順序を変更することもできます。 例えば、CompareKeyValuePairを次のように変更することにより、Dictionaryを値でソートできるようになります。
なお、上記の例ではキーを比較したためDictionaryはキーを基準にソートされましたが、CompareKeyValuePairを次のようにすることで値でソートできるようになります。
 

        

        
 
#tabpage(codelang=cs,container-title=Dictionaryを値でソートする例)
#tabpage(codelang=cs,container-title=Dictionaryを値でソートする例)
 
#code{{
#code{{
787,10 579,6
 
Charlie 5
Charlie 5
 
}}
}}
 

        

        
+
#remarks
+
List.Sortメソッドと比較方法の定義(Comparison<T>デリゲート)については、[[programming/netfx/sorting/1_compositetypes]]で詳しく解説します。
+
#remarks-end
+

          
 
***キーと値の配列に変換してソートする
***キーと値の配列に変換してソートする
 
2つ目は、Dictionaryの内容を、キーと値で個別の配列に変えてからソートする方法です。
2つ目は、Dictionaryの内容を、キーと値で個別の配列に変えてからソートする方法です。
 

        

        
821,9 609,7
 
|"Eve"|⇔|4|
|"Eve"|⇔|4|
 
#column-end
#column-end
 

        

        
~
&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};には、キーと値のそれぞれが格納された二つの配列を渡すと、キーの配列に従って対応する値の配列も同時に並べ替えることができるオーバーロードが用意されています。 これを使うことにより、Dictionaryのキーと値の両方を格納した配列を互いに関連付けた状態のままソートすることが出来ます。 KeyValuePairは使わないため、''キーが単純型であれば''ソート方法(比較方法)を定義する必要がありません。
&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};には、それぞれにキーと値が格納された二つの配列を渡すと、キーの配列に従って値の配列を並べ替えることができるオーバーロードが用意されています。 これを使うことでDictionaryのソートを行うことが出来ます。 KeyValuePairは使わないため、キーが単純型であればソート方法(比較方法)を定義する必要がありません。
+

          
+
この方法によるソートの具体例は次のようになります。
 

        

        
 
#tabpage(codelang=cs,container-title=Array.Sortを使ったDictionaryのソート)
#tabpage(codelang=cs,container-title=Array.Sortを使ったDictionaryのソート)
 
#code{{
#code{{
900,7 686,7
 
Eve 4
Eve 4
 
}}
}}
 

        

        
~
なお、Array.Sortに渡す配列はキーの配列・値の配列の順となっています。 この順序を逆に指定すれば、値でソートできるようになります。
なお、Array.Sortに渡す配列はキーの配列・値の配列の順となっていますが、この順序と逆に指定することで値でソートできるようになります。
 

        

        
 
#tabpage(codelang=cs,container-title=Dictionaryを値でソートする例)
#tabpage(codelang=cs,container-title=Dictionaryを値でソートする例)
 
#code{{
#code{{
940,9 726,9
 
拡張メソッドであるOrderByメソッドを使うことでもDictionaryをソートできます。 [[OrderByメソッドを使ったDictionaryのソート>#Dictionary.OrderBy]]については後述します。
拡張メソッドであるOrderByメソッドを使うことでもDictionaryをソートできます。 [[OrderByメソッドを使ったDictionaryのソート>#Dictionary.OrderBy]]については後述します。
 

        

        
 
**Enumerable.OrderBy [#Enumerable.OrderBy]
**Enumerable.OrderBy [#Enumerable.OrderBy]
~
&msdn(netfx,type,System.Collections.Generic.IEnumerable`1){IEnumerable<T>};インターフェイスを実装しているコレクションの場合、拡張メソッドである&msdn(netfx,member,System.Core.dll,System.Linq.Enumerable.OrderBy){OrderBy};を使うことによってソートする事ができます。
IEnumerable<T>を実装しているコレクションの場合、拡張メソッドである&msdn(netfx,member,System.Core.dll,System.Linq.Enumerable.OrderBy){OrderBy};を使うことでもソートする事ができます。
 

        

        
~
配列や、ListやDictionaryを含むジェネリックコレクションクラスはいずれもIEnumerable<T>を実装しているため、OrderByメソッドでソートする事ができます。
配列やList、Dictionaryを含め、&msdn(netfx,ns,System.Collections.Generic){System.Collections.Generic名前空間};にあるコレクションクラスはいずれもIEnumerable<T>を実装しているため、OrderByメソッドでソートする事ができます。
 

        

        
 
***OrderByメソッドの使い方と動作
***OrderByメソッドの使い方と動作
 
OrderByメソッドを使って配列・Listをソートする例について見ていきます。
OrderByメソッドを使って配列・Listをソートする例について見ていきます。
1037,18 823,13
 
1, 2, 3, 4, 5, 
1, 2, 3, 4, 5, 
 
}}
}}
 

        

        
~
[[Array.Sortメソッド>#Array.Sort]]や[[List.Sortメソッド>#List.Sort]]とは異なり、OrderByメソッドは元の配列・コレクションの状態を変更しません(非破壊的なソート)。 代わりにソート後の結果となる&msdn(netfx,type,System.Core.dll,System.Linq.IOrderedEnumerable`1){IOrderedEnumerable<TElement>};を返します。
Array.SortメソッドやList.Sortメソッドとは異なり、OrderByメソッドは元の配列・コレクションの状態を変更しません。 代わりにソート後の結果となる&msdn(netfx,type,System.Core.dll,System.Linq.IOrderedEnumerable`1){IOrderedEnumerable<TElement>};を返します。 なお、OrderByは''遅延実行''されるため、戻り値に対してforeach等で列挙操作を行うことで初めてソートが行われます。 IOrderedEnumerable<TElement>はIEnumerable<T>から派生したインターフェイスなので、遅延実行される点を除けば違いを特に意識しなくてもIEnumerable<T>と同様に扱えます。
+

          
+
IOrderedEnumerable<TElement>はIEnumerable<T>から派生したインターフェイスで、後述する点を除けば違いを特に意識しなくてもIEnumerable<T>と同様に扱えます。 ほとんどの場合、この戻り値を直接列挙したり、他のコレクションに格納しなおしたりして使用することになります。
 

        

        
 
#remarks
#remarks
 
IEnumerable<T>については[[programming/netfx/enumerator/0_enumerable_enumerator]]を参照してください。
IEnumerable<T>については[[programming/netfx/enumerator/0_enumerable_enumerator]]を参照してください。
 
#remarks-end
#remarks-end
 

        

        
~
OrderByメソッドは''遅延実行''されるため、戻り値のIOrderedEnumerable<TElement>に対してforeach等による''列挙操作を行うことで初めてソートが行われます''。 戻り値を即座に列挙したり、他のコレクションに格納する場合は特にこの動作を意識する必要はありませんが、[[#Enumerable.OrderBy_modify_collection]]で解説するように扱い方によっては意図に反する動作となる場合もあるので、''遅延実行''となることとその動作については把握しておく必要があります。
OrderByメソッドの引数には、ソートの際に使用するキーを選択するメソッドへのデリゲート(またはラムダ式)を指定します。 先の例はラムダ式を用いたものですが、これをメソッドとして展開すると次のようになります。
+

          
+
***OrderByメソッドとキーの選択 [#Enumerable.OrderBy_KeySelector]
+
OrderByメソッドの引数&var{keySelector};には、''ソートの際に使用するキーを選択するメソッド''へのデリゲート(またはラムダ式)を指定します。 先の例ではラムダ式を用いていましたが、これをメソッドとして展開すると次のようになります。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
1056,10 837,9
 
using System.Linq;
using System.Linq;
 

        

        
 
class Sample {
class Sample {
+
  // ソートの際に使用するキーを選択するメソッド
 
  static int KeySelector(int val)
  static int KeySelector(int val)
 
  {
  {
~
    // 要素の値そのものをソートの際のキーとする
    // 要素の値そのものを並べ替えの際のキーとする
 
    return val;
    return val;
 
  }
  }
 

        

        
1084,9 864,8
 
Imports System.Linq
Imports System.Linq
 

        

        
 
Class Sample
Class Sample
+
  ' ソートの際に使用するキーを選択するメソッド
 
  Shared Function KeySelector(ByVal val As Integer) As Integer
  Shared Function KeySelector(ByVal val As Integer) As Integer
~
    ' 要素の値そのものをソートの際のキーとする
    ' 要素の値そのものを並べ替えの際のキーとする
 
    Return val
    Return val
 
  End Function
  End Function
 

        

        
1106,55 885,10
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
~
OrderByメソッドの引数&var{keySelector};とキー選択のメソッドについて詳しく見ていきます。
OrderByメソッドの引数についてもう少し詳しく見ていきます。 OrderByメソッドではソートの際に何をキーとしてソートするかを指定する必要があります。 これは、単純型でも複合型でもソートできるようにするために必要となるものです。 単純型の配列・コレクションの場合は単に''要素そのものをキー''とすればよいため、上記の例のように一見するとあまり意味のない記述となるのですが、Dictionaryや複合型の配列・コレクションの場合は、何を基準にソートするかによってキーを定める必要があるため、この引数が意味を持つようになります。
+

          
+
OrderByメソッドでは、''ソートの際に何をキーとしてソートするか''を引数&var{keySelector};として指定する必要があります。 これは、単純型でも複合型でもソートできるようにするために必要となるものです。 数値や文字列など単純型の配列・コレクションの場合は、単に''要素そのもの''(つまり数値の値や文字列自体)をキーとすればよいため、上記の例のように一見するとあまり意味のない記述となります。 しかし、Dictionaryや複合型の配列・コレクションの場合は、何を基準にソートするかによってキーを定める必要があるため、この引数が意味を持つようになります。
+

          
+
例として、文字列型のNameというプロパティを持つAccountクラスをソートしたい場合は、次のようにキー選択のメソッドを定義する必要があります。 この例を見れば、&var{keySelector};の持つ意味が理解しやすくなると思います。
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
// Accountクラスのソートの際に使用するキーを選択するメソッド
+
static string KeySelector(Account val)
+
{
+
  // Account.Nameプロパティの値をソートの際のキーとする
+
  return val.Name;
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
' Accountクラスのソートの際に使用するキーを選択するメソッド
+
Shared Function KeySelector(ByVal val As Account) As String
+
  ' Account.Nameプロパティの値をソートの際のキーとする
+
  Return val.Name
+
End Function
+
}}
+
#tabpage-end
+

          
+
比較のために、先の例におけるキー選択のメソッドを抜粋します。 このメソッドでは、int/Integer型のソートをするために、そのキーとしてint/Integerの持つ値を選択しています。
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
static int KeySelector(int val)
+
{
+
  return val;
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Shared Function KeySelector(ByVal val As Integer) As Integer
+
  Return val
+
End Function
+
}}
+
#tabpage-end
+

          
+
このように、&var{keySelector};には''引数にソート対象をとり、戻り値としてソート対象のキーを返すメソッド(またラムダ式)''を指定します。
+

          
 

        

        
 
***OrderByメソッドを使ったDictionaryのソート [#Dictionary.OrderBy]
***OrderByメソッドを使ったDictionaryのソート [#Dictionary.OrderBy]
~
OrderByメソッドを使った具体的な例として、Dictionaryのソートを行う例について見てみます。
具体的な例として、OrderByメソッドを使ってDictionaryのソートを行う例について見てみます。 次の例のメソッドKeySelectorはキーの選択を行うメソッドです。 Dictionaryの場合は個々の要素がKeyValuePairとして格納されているため、''KeyValuePairの何をキーとするか''を定める必要があります。 Dictionaryのキーでソートするためには、KeyValuePair.Keyプロパティを並べ替えの際のキーとするようにします。
+

          
+
次の例のメソッドKeySelectorはキーの選択を行うメソッドです。 Dictionaryの場合は個々の要素がKeyValuePairとして格納されているため、''KeyValuePairの何をキーとするか''を定める必要があります。 Dictionaryをキーでソートするためには、KeyValuePair.Keyプロパティを並べ替えの際のキーとするようにします。 KeySelectorメソッドでは、このようなキー選択の動作を定義しています。 OrderByメソッドの引数にKeySelectorメソッドを渡すことにより、その定義にしたがってソートが行われます。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
1230,7 964,7
 
Eve 4
Eve 4
 
}}
}}
 

        

        
~
比較のために、ラムダ式を使ったものに書き換えると次のようになります。 OrderByメソッドで指定しているラムダ式は、上記の例におけるKeySelectorメソッドと同じ動作をします。 ラムダ式を使用すると、キーを選択するコードを個別のメソッドとして記述する必要がなくなるため、よりシンプルに記述できます。
なお、比較のために上記の例をラムダ式を使ったものに書き換えると次のようになります。 キーを選択するコードをメソッドとして記述する必要がないため、よりシンプルに記述できます。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
1288,7 1022,7
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
~
上記2つの例ではDictionaryをキーにしたがってソートするためにKeyValuePair.Keyをキーとしていました。 一方、Dictionaryを値にしたがってソートしたい場合は、次のようにKeyValuePair.ValueをキーとするようにKeySelectorを変えるだけで出来ます(キーの型に合わせてメソッドの戻り値を変えている点に注意してください)。
ここまで例ではKeyValuePair.Keyをキーとしましたが、Dictionaryを値でソートしたい場合は、次のようにKeyValuePair.ValueをキーとするようにKeySelectorを変えるだけで出来ます(キーの型に合わせてメソッドの戻り値を変えている点に注意してください)。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
1319,14 1053,10
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
~
#remarks
なお、OrderByメソッドの代わりに[[OrderByDescendingメソッド>#Enumerable.OrderByDescending]]を使うと、昇順ではなく降順でソートすることができます。
+
OrderByメソッドの代わりに[[OrderByDescendingメソッド>#Enumerable.OrderByDescending]]を使うと、昇順ではなく降順でソートすることができます。
+
#remarks-end
+

          
+
***元のコレクションに対する変更とOrderByメソッドの結果への影響 [#Enumerable.OrderBy_modify_collection]
+
OrderByメソッドは''遅延実行''されるため、メソッドを呼び出した時点では結果は確定していません。 OrderByメソッドの戻り値を列挙する(もしくは配列・リストに変換する)時点ではじめて結果が確定します。
 

        

        
~
そのため、次の例のように、OrderByメソッドを呼び出してその結果が確定する前に元のコレクションに変更を加えると、その変更はソート後の結果にも影響するという点に注意が必要です。
***OrderByメソッドの結果と元のコレクションへの変更
-
OrderByメソッドは''遅延実行''されるため、メソッドを呼び出した時点では結果は確定していません。 戻り値を列挙する(もしくは配列・リストに変換する)時点ではじめて結果が確定します。 次の例のように、OrderByメソッドを呼び出した後に元のコレクションに変更を加えると、その変更はソート後の結果にも影響するという点に注意が必要です。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
1397,7 1127,7
 
ここでは降順(大きい順)でのソートについて、いくつかの方法を見ていきます。
ここでは降順(大きい順)でのソートについて、いくつかの方法を見ていきます。
 

        

        
 
**Sort + Reverse [#SortReverse]
**Sort + Reverse [#SortReverse]
~
1つ目は、Sortメソッドで昇順にソートしてから、Reverseメソッドで逆順(つまり降順)にするものです。 配列・ArrayList・List<T>にはそれぞれ&msdn(netfx,member,System.Array.Reverse){Array.Reverse};, &msdn(netfx,member,System.Collections.ArrayList.Reverse){ArrayList.Reverse};, &msdn(netfx,member,System.Collections.Generic.List`1.Reverse){List.Reverse};の各メソッドが用意されていて、これを使うことで要素を逆順に並び替える事が出来ます。
1つ目は、Sortメソッドで昇順にソートしてから、Reverseメソッドで逆順(つまり降順)にするものです。 配列・ArrayList・List<T>にはそれぞれ&msdn(netfx,member,System.Array.Reverse){Array.Reverse};, &msdn(netfx,member,System.Collections.ArrayList.Reverse){ArrayList.Reverse};, &msdn(netfx,member,System.Collections.Generic.List`1.Reverse){List.Reverse};の各メソッドが用意されていて、これを使うことで要素を逆順に並び替える事が出来ます。 なお、これらのメソッドはSortメソッドと同様、元になった配列・コレクション自体の内容を逆順にします。
 

        

        
 
#tabpage(codelang=cs,container-title=配列を降順にソートする)
#tabpage(codelang=cs,container-title=配列を降順にソートする)
 
#code{{
#code{{
1497,14 1227,8
 
5, 4, 3, 2, 1, 
5, 4, 3, 2, 1, 
 
}}
}}
 

        

        
+
Reverseメソッドそのものは単に要素を逆順に並び替えるだけなので、Reverseメソッドだけを呼び出しても降順には並び替えられません。 必ずSortメソッドで昇順にソートしてから呼び出す必要があります。
+

          
+
Reverseメソッドは、[[Array.Sort>#Array.Sort]]や[[List.Sort>#List.Sort]]メソッドと同様、''元になった配列・コレクション自体の内容を逆順にします''(破壊的変更)。 非破壊的な方法で降順にソートしたい(元の配列・コレクションは維持したままにしたい)場合は、[[OrderByDescending>#Enumerable.OrderByDescending]]メソッドを使用することができます。
+

          
 
**Sort + Comparison<T> [#DescendingComparison]
**Sort + Comparison<T> [#DescendingComparison]
~
2つ目は、Sortメソッドと&msdn(netfx,type,System.Comparison`1){Comparison<T>デリゲート};を組み合わせる方法です。 Sortメソッドに特に引数を指定しない場合、Sortメソッドはデフォルト(つまり昇順)の並び替え順序でソートします。 Sortメソッドの引数に並び替え順序を定義したメソッドを表すComparison<T>デリゲートを指定すると、その順序に従ってソートするようになります。
2つ目は、Sortメソッドと&msdn(netfx,type,System.Comparison`1){Comparison<T>デリゲート};を組み合わせる方法です。 Sortメソッドに特に引数を指定しない場合、デフォルト(つまり昇順)の並び替え順序でソートします。 Sortメソッドの引数に並び替え順序を定義したメソッドを表すComparisonデリゲートを指定すると、その順序に従ってソートするようになります。 よって、降順に並び替えるよう動作を定義するメソッドを用意してSortメソッドに渡すことで、降順にソートする事ができます。 なお、この方法はComparisonデリゲートを引数にとることが出来る&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};と&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};で使うことが出来ます。
+

          
+
したがって、降順に並び替えるよう動作を定義するメソッドを用意してそれをSortメソッドに渡すことにより、降順にソートする事ができます。 なお、この方法はComparison<T>デリゲートを引数にとることが出来る&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};と&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};で使うことが出来ます。 (&msdn(netfx,member,System.Collections.ArrayList.Sort){ArrayList.Sortメソッド};ではIComparerを指定する必要があります)
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
1516,7 1240,7
 
  static int CompareDescending(int x, int y)
  static int CompareDescending(int x, int y)
 
  {
  {
 
    return y.CompareTo(x);
    return y.CompareTo(x);
~
    // 単に次のようにしても同じ結果となる
    // 単に次のようにしても同じ
 
    // return y - x;
    // return y - x;
 
    // return -x.CompareTo(y);
    // return -x.CompareTo(y);
 

        

        
1549,7 1273,7
 
  ' 二つのIntegerを比較するためのメソッド
  ' 二つのIntegerを比較するためのメソッド
 
  Shared Function CompareDescending(ByVal x As Integer, ByVal y As Integer) As Integer
  Shared Function CompareDescending(ByVal x As Integer, ByVal y As Integer) As Integer
 
    Return y.CompareTo(x)
    Return y.CompareTo(x)
~
    ' 単に次のようにしても同じ結果となる
    ' 単に次のようにしても同じ
 
    ' Return y - x
    ' Return y - x
 
    ' Return -x.CompareTo(y)
    ' Return -x.CompareTo(y)
 

        

        
1579,13 1303,7
 
5, 4, 3, 2, 1, 
5, 4, 3, 2, 1, 
 
}}
}}
 

        

        
+
#remarks
 
SortメソッドとComparison<T>デリゲートについては、[[programming/netfx/sorting/1_compositetypes]]でも改めて解説します。
SortメソッドとComparison<T>デリゲートについては、[[programming/netfx/sorting/1_compositetypes]]でも改めて解説します。
+
#remarks-end
+

          
+
#remarks
+
Sortメソッドと型ごとのデフォルトのソート順については[[programming/netfx/sorting/0_basictypes_1_defaultsortorder]]で解説しています。
+
#remarks-end
 

        

        
 
**OrderByDescending [#Enumerable.OrderByDescending]
**OrderByDescending [#Enumerable.OrderByDescending]
 
3つ目は、拡張メソッドである&msdn(netfx,member,System.Core.dll,System.Linq.Enumerable.OrderByDescending){OrderByDescendingメソッド};を使う方法です。 [[既に解説したOrderByメソッド>#Enumerable.OrderBy]]は配列・コレクションを昇順にソートするものでしたが、OrderByDescendingメソッドは降順にソートするものです。
3つ目は、拡張メソッドである&msdn(netfx,member,System.Core.dll,System.Linq.Enumerable.OrderByDescending){OrderByDescendingメソッド};を使う方法です。 [[既に解説したOrderByメソッド>#Enumerable.OrderBy]]は配列・コレクションを昇順にソートするものでしたが、OrderByDescendingメソッドは降順にソートするものです。
1672,7 1390,7
 
5, 4, 3, 2, 1, 
5, 4, 3, 2, 1, 
 
}}
}}
 

        

        
~
降順にソートされる点を除けばOrderByDescendingメソッドはOrderByメソッドと同じなので、引数や動作について詳しくは[[OrderByメソッドについての解説>#Enumerable.OrderBy]]を参照してください。
OrderByDescendingメソッドは、降順にソートされる点以外はOrderByメソッドと同じなので、詳しくは[[OrderByメソッドについての解説>#Enumerable.OrderBy]]を参照してください。
 

        

        
 

        

        
 

        

        

programming/netfx/sorting/0_basictypes_1_defaultsortorder/index.wiki.txt

current previous
18,10 18,11
 
#adunit
#adunit
 

        

        
 
*数値型 [#DefaultSortOrder_Numerics]
*数値型 [#DefaultSortOrder_Numerics]
~
int, long, uint, byte等の整数型、float, double, decimal等の実数型は、いずれもその''数の小さい順''に並べ替えられます。
int, long, uint, byte等の整数型、float, double, decimal等の実数型は、いずれもその数の小さい順に並べ替えられます。 float, doubleの場合、NaN(非数)は-∞(負の無限大)や他の数よりも小さいと扱われる点に注意が必要です。
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
 
int: -2147483648, -1, 0, 1, 2147483647, 
int: -2147483648, -1, 0, 1, 2147483647, 
-
double: NaN, -Infinity, -1.79769313486232E+308, -1, -0.01, 0, 0.01, 1, 1.79769313486232E+308, Infinity, 
 
decimal: -79228162514264337593543950335, -1, -0.01, 0, 0.01, 1, 79228162514264337593543950335, 
decimal: -79228162514264337593543950335, -1, -0.01, 0, 0.01, 1, 79228162514264337593543950335, 
 
}}
}}
 

        

        
33,12 34,18
 
  static void Main()
  static void Main()
 
  {
  {
 
    int[] intArray = new int[] {0, 1, -1, int.MaxValue, int.MinValue};
    int[] intArray = new int[] {0, 1, -1, int.MaxValue, int.MinValue};
-
    double[] doubleArray = new double[] {
-
      0.0, 1.0, -1.0, 0.01, -0.01,
-
      double.MaxValue, double.MinValue,
-
      double.PositiveInfinity, double.NegativeInfinity, double.NaN,
-
    };
 
    decimal[] decimalArray = new decimal[] {
    decimal[] decimalArray = new decimal[] {
 
      0m, 1m, -1m, 0.01m, -0.01m,
      0m, 1m, -1m, 0.01m, -0.01m,
 
      decimal.MaxValue, decimal.MinValue,
      decimal.MaxValue, decimal.MinValue,
 
    };
    };
 

        

        
 
    Array.Sort(intArray);
    Array.Sort(intArray);
-
    Array.Sort(doubleArray);
 
    Array.Sort(decimalArray);
    Array.Sort(decimalArray);
 

        

        
 
    Console.Write("int: ");
    Console.Write("int: ");
47,6 54,12
 
    }
    }
 
    Console.WriteLine();
    Console.WriteLine();
 

        

        
-
    Console.Write("double: ");
-
    foreach (double val in doubleArray) {
-
      Console.Write("{0}, ", val);
-
    }
-
    Console.WriteLine();
-

          
 
    Console.Write("decimal: ");
    Console.Write("decimal: ");
 
    foreach (decimal val in decimalArray) {
    foreach (decimal val in decimalArray) {
 
      Console.Write("{0}, ", val);
      Console.Write("{0}, ", val);
62,12 75,18
 
Class Sample
Class Sample
 
  Shared Sub Main()
  Shared Sub Main()
 
    Dim intArray As Integer() = New Integer() {0, 1, -1, Integer.MaxValue, Integer.MinValue}
    Dim intArray As Integer() = New Integer() {0, 1, -1, Integer.MaxValue, Integer.MinValue}
-
    Dim doubleArray As Double() = New Double() { _
-
      0.0, 1.0, -1.0, 0.01, -0.01, _
-
      double.MaxValue, double.MinValue, _
-
      double.PositiveInfinity, double.NegativeInfinity, double.NaN _
-
    }
 
    Dim decimalArray As Decimal() = New Decimal() { _
    Dim decimalArray As Decimal() = New Decimal() { _
 
      0D, 1D, -1D, 0.01D, -0.01D, _
      0D, 1D, -1D, 0.01D, -0.01D, _
 
      Decimal.MaxValue, Decimal.MinValue _
      Decimal.MaxValue, Decimal.MinValue _
 
    }
    }
 

        

        
 
    Array.Sort(intArray)
    Array.Sort(intArray)
-
    Array.Sort(doubleArray)
 
    Array.Sort(decimalArray)
    Array.Sort(decimalArray)
 

        

        
 
    Console.Write("Integer: ")
    Console.Write("Integer: ")
76,162 95,15
 
    Next
    Next
 
    Console.WriteLine()
    Console.WriteLine()
 

        

        
+
    Console.Write("Decimal: ")
+
    For Each val As Decimal In decimalArray
+
      Console.Write("{0}, ", val)
+
    Next
+
    Console.WriteLine()
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
**浮動小数点型
+
float, doubleの場合、''NaN(非数)は-∞(負の無限大)や他の数よりも小さい''と扱われる点に注意が必要です。
+

          
+
#prompt(実行結果){{
+
double: NaN, -Infinity, -1.79769313486232E+308, -1, -0.01, 0, 0.01, 1, 1.79769313486232E+308, Infinity, 
+
}}
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    double[] doubleArray = new double[] {
+
      0.0, 1.0, -1.0, 0.01, -0.01,
+
      double.MaxValue, double.MinValue,
+
      double.PositiveInfinity, double.NegativeInfinity, double.NaN,
+
    };
+

          
+
    Array.Sort(doubleArray);
+

          
+
    Console.Write("double: ");
+
    foreach (double val in doubleArray) {
+
      Console.Write("{0}, ", val);
+
    }
+
    Console.WriteLine();
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim doubleArray As Double() = New Double() { _
+
      0.0, 1.0, -1.0, 0.01, -0.01, _
+
      double.MaxValue, double.MinValue, _
+
      double.PositiveInfinity, double.NegativeInfinity, double.NaN _
+
    }
+

          
+
    Array.Sort(doubleArray)
+

          
 
    Console.Write("Double: ")
    Console.Write("Double: ")
 
    For Each val As Double In doubleArray
    For Each val As Double In doubleArray
 
      Console.Write("{0}, ", val)
      Console.Write("{0}, ", val)
 
    Next
    Next
 
    Console.WriteLine()
    Console.WriteLine()
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
 

        

        
~
**ヌル許容の数値型 [#DefaultSortOrder_Numerics_Nullable]
    Console.Write("Decimal: ")
~
ヌル許容の数値型の場合、''``null``/``Nothing``は他のどの数よりも小さい''と扱われます。
    For Each val As Decimal In decimalArray
~

          
      Console.Write("{0}, ", val)
+
#prompt(実行結果){{
+
(null), -2147483648, -1, 0, 1, 2147483647,
+
}}
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    int?[] arr = new int?[] {0, 1, -1, null, int.MaxValue, int.MinValue};
+

          
+
    Array.Sort(arr);
+

          
+
    foreach (int? val in arr) {
+
      Console.Write("{0}, ", val == null ? "(null)" : val.ToString());
+
    }
+
    Console.WriteLine();
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim arr As Integer?() = New Integer?() {0, 1, -1, Nothing, Integer.MaxValue, Integer.MinValue}
+

          
+
    Array.Sort(arr)
+

          
+
    For Each val As Integer? In arr
+
      If val Is Nothing Then
+
        Console.Write("{0}, ", "(Nothing)")
+
      Else
+
        Console.Write("{0}, ", val)
+
      End If
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
またNaNに対しても、''``null``/``Nothing``はNaNよりも小さい''と扱われます。
+

          
+
#prompt(実行結果){{
+
(null), NaN, -Infinity, -1.79769313486232E+308, -1, 0, 1,
+
}}
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    double?[] arr = new double?[] {
+
      0.0, 1.0, -1.0, null, double.MinValue, double.NegativeInfinity, double.NaN,
+
    };
+

          
+
    Array.Sort(arr);
+

          
+
    foreach (double? val in arr) {
+
      Console.Write("{0}, ", val == null ? "(null)" : val.ToString());
+
    }
+
    Console.WriteLine();
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim arr As Double?() = New Double?() { _
+
      0.0, 1.0, -1.0, Nothing, _
+
      double.MinValue, double.NegativeInfinity, double.NaN _
+
    }
+

          
+
    Array.Sort(arr)
+

          
+
    For Each val As Double? In arr
+
      If val Is Nothing Then
+
        Console.Write("{0}, ", "(Nothing)")
+
      Else
+
        Console.Write("{0}, ", val)
+
      End If
 
    Next
    Next
 
    Console.WriteLine()
    Console.WriteLine()
 
  End Sub
  End Sub
298,57 170,7
 
ただ、カルチャや比較方法によっては単なる辞書順にはならないので注意してください。 詳しくは[[programming/netfx/string/2_2_compareoptions]]や[[programming/netfx/locale/1_infoes]]で解説しています。
ただ、カルチャや比較方法によっては単なる辞書順にはならないので注意してください。 詳しくは[[programming/netfx/string/2_2_compareoptions]]や[[programming/netfx/locale/1_infoes]]で解説しています。
 
#remarks-end
#remarks-end
 

        

        
~
``null``/``Nothing``や''空の文字列''(長さ0の文字列)が含まれていても並べ替えることができます。 ``null``/``Nothing``は、''空の文字列を含む他のどの文字列よりも小さい''と扱われます。 空の文字列を含めて大小関係を並べると、「&var{null/Nothing}; < &var{空の文字列}; < &var{1文字以上の文字列};」となります。
**大文字小文字の違いを考慮したソート
+

          
+
#prompt(実行結果){{
+
(null)
+

          
+
a
+
aa
+
ab
+
b
+
}}
+

          
+
#tabpage(codelang=cs)
+
#code{{
+
using System;
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    string[] arr = new string[] {
+
      "a", "aa", "ab", "b", null, ""
+
    };
+

          
+
    Array.Sort(arr);
+

          
+
    foreach (string val in arr) {
+
      Console.WriteLine(val ?? "(null)");
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+

          
+
Class Sample
+
  Shared Sub Main()
+
    Dim arr As String() = New String() { _
+
      "a", "aa", "ab", "b", Nothing, "" _
+
    }
+

          
+
    Array.Sort(arr)
+

          
+
    For Each val As String In arr
+
      Console.WriteLine(If(val, "(Nothing)"))
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
**大文字小文字の違いを考慮したソート [#DefaultSortOrder_String_IgnoreCase]
 
文字列をソートする際、場合によっては大文字小文字の違いをどう扱うか厳密に決める必要が出て来ます。 その場合は、&msdn(netfx,type,System.StringComparer){StringComparer};を使ってどう扱うかを指定することができます。 Sortメソッド、OrderByメソッドともにIComparerを引数に取ることができ、これにStringComparerを指定することで、文字列比較の際の動作を指定できます。
文字列をソートする際、場合によっては大文字小文字の違いをどう扱うか厳密に決める必要が出て来ます。 その場合は、&msdn(netfx,type,System.StringComparer){StringComparer};を使ってどう扱うかを指定することができます。 Sortメソッド、OrderByメソッドともにIComparerを引数に取ることができ、これにStringComparerを指定することで、文字列比較の際の動作を指定できます。
 

        

        
 
#tabpage(codelang=cs,container-title=Sort + IComparer)
#tabpage(codelang=cs,container-title=Sort + IComparer)
477,11 299,7
 

        

        
 

        

        
 
*日付型 [#DefaultSortOrder_DateTime]
*日付型 [#DefaultSortOrder_DateTime]
~
DateTimeとDateTimeOffsetは''日付・時間の古い順''に並べ替えられます。
DateTimeとDateTimeOffsetは日付・時間の古い順に並べ替えられます。 並べ替えに際して、DateTimeでは&msdn(netfx,member,System.DateTime.Kind){Kindプロパティ};の値は無視されて日時のみが比較され、DateTimeOffsetでは日時に加えて&msdn(netfx,member,System.DateTimeOffset.Offset){Offsetプロパティ};の値が考慮されて比較される点に注意が必要です。 DateTimeの場合、[[ToLocalTime・ToUniversalTimeメソッド>programming/netfx/datetime/1_kind_offset_timezone#DateTime_ToLocalTimeToUniversalTime]]を使って現地時刻・UTCのいずれかに統一してからソートしないと意図しない結果になる場合があります。
+

          
+
並べ替えに際して、DateTimeでは&msdn(netfx,member,System.DateTime.Kind){Kindプロパティ};の値は無視されて日時のみが比較されます。 したがって、``2000-01-01T00:00:00+00:00``(UTC)と``2000-01-01T00:00:00+09:00``(ローカル時刻)と``2000-01-01T00:00:00``(タイムゾーン指定なし)はいずれも同一の値として扱われます。 DateTimeの並べ替えでは、自動的にUTCに変換されてから並べ替えられるといったことはありません。 そのため、[[ToLocalTime・ToUniversalTimeメソッド>programming/netfx/datetime/1_kind_offset_timezone#DateTime_ToLocalTimeToUniversalTime]]を使って現地時刻・UTCのいずれかに統一してからソートしないと意図しない結果になる場合があります。
+

          
+
DateTimeOffsetでは、日時に加えて&msdn(netfx,member,System.DateTimeOffset.Offset){Offsetプロパティ};の値が考慮されて比較されます。 したがって、``2000-01-01T00:00:00+09:00``、``2000-01-01T00:00:00+00:00``、``2000-01-01T00:00:00-05:00``はいずれも異なる値として比較されます。
 

        

        
 
#prompt(実行結果){{
#prompt(実行結果){{
 
DateTime
DateTime

programming/netfx/sorting/1_compositetypes/index.wiki.txt

current previous
18,22 18,20
 
#adunit
#adunit
 

        

        
 
*複合型のソート [#SortCompositeType]
*複合型のソート [#SortCompositeType]
~
数値や文字列などの型は、あらかじめ[[デフォルトの並べ替え順序が定義されている>programming/netfx/sorting/0_basictypes_1_defaultsortorder]]ため特に何も指定しなくても[[Array.SortやList.Sort>programming/netfx/sorting/0_basictypes]]などのメソッドでソートを行うことが出来ます。 一方、独自に定義したクラス・構造体などの''複合型''(1つ以上の基本型を組み合わせて構成される型)の場合は、並べ替え順序を個別に定義しない限りソートすることが出来ません。
数値や文字列などの型は、あらかじめデフォルトの並べ替え順序が定義されているため特に何も指定しなくてもソートを行うことが出来ます。 一方、独自に定義したクラス・構造体などの複合型の場合は、並べ替え順序を個別に定義しない限りソートすることが出来ません。
 

        

        
 
#remarks
#remarks
~
仮に型が比較演算子をオーバーロードしていてもそれだけではソート出来るようにはなりません。 この点については、[[programming/netfx/comparison/0_relational_operator_overload]]で詳しく解説しています。
仮に型が比較演算子をオーバーロードしていてもそれだけではソート出来るようにはなりません。 この点については、[[programming/netfx/comparison/0_comparison]]で詳しく解説しています。
 
#remarks-end
#remarks-end
 

        

        
 
ここでは、複合型をソートする方法・並べ替え順序を定義する方法について解説します。
ここでは、複合型をソートする方法・並べ替え順序を定義する方法について解説します。
 

        

        
 
**Sort + Comparison<T>
**Sort + Comparison<T>
~
[[降順でのソート>programming/netfx/sorting/0_basictypes#DescendingComparison]]でも述べているとおり、&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};および&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};では、並び替え順序を定義したメソッドを用意し、それを&msdn(netfx,type,System.Comparison`1){Comparison<T>デリゲート};としてSortメソッドの引数に指定することにより、デフォルト(昇順)以外の順序、独自に定義した順序でソートすることが出来ます。
[[降順でのソート>programming/netfx/sorting/0_basictypes#DescendingComparison]]でも述べたとおり、&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};および&msdn(netfx,member,System.Collections.Generic.List`1.Sort){List.Sortメソッド};では、並び替え順序を定義したメソッドを用意し、それを&msdn(netfx,type,System.Comparison`1){Comparison<T>デリゲート};としてSortメソッドの引数に指定することにより、デフォルト(昇順)以外の順序、独自に定義した順序でソートすることが出来ます。
 

        

        
~
早速、独自に定義したクラスをソートする方法、並べ替え順序を定義する方法について順を追って見ていきます。
早速、独自に定義したクラスをソートする方法、並べ替え順序を定義する方法について順を追って見ていきます。 ここではNameフィールド(string)とIDフィールド(int)を持つクラスAccountを作成し、Accountクラスのインスタンスが格納されたリストをID順にソートすることにします。 具体的には、次のコードでソートが出来るように実装していきます。
 

        

        
~
ここではNameフィールド(string)とIDフィールド(int)を持つクラスAccountを作成し、Accountクラスのインスタンスが格納されたリストをID順にソートすることにします。 具体的には、次の不完全なコードでソートが出来るように実装していきます。
#tabpage(codelang=cs)
+

          
+
#tabpage(codelang=cs,container-title=ソートを行いたいコード)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Collections.Generic;
using System.Collections.Generic;
107,7 105,7
 

        

        
 
まずは、並べ替え順序を定義をするために、比較を行うメソッドを用意します。 このメソッドは、Comparison<T>デリゲートの形でSortメソッドに渡す必要があります。 Comparison<T>デリゲートの形式を見てみると、次のようになっています。
まずは、並べ替え順序を定義をするために、比較を行うメソッドを用意します。 このメソッドは、Comparison<T>デリゲートの形でSortメソッドに渡す必要があります。 Comparison<T>デリゲートの形式を見てみると、次のようになっています。
 

        

        
~
#tabpage(codelang=cs,container-title=Comparison<T>デリゲート)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
public delegate int Comparison<in T>(T x, T y);
public delegate int Comparison<in T>(T x, T y);
 
}}
}}
117,19 115,17
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
~
この形式を分かりやすく説明すると、「ある型Tの要素二つを比べて、どちらが小さいか・大きいかをintの数値で返す」というものです。 Sortメソッドの内部では、各要素の比較が行われる度にこのメソッドが呼び出され、戻り値として返される大小関係によって要素の並べ替えが行われることになります。 この形式にあわせて、Accountクラスを比較するメソッドCompareAccountのひな形を作成すると次のようになります。
この形式を分かりやすく説明すると、「ある型Tの要素二つを比べて、どちらが小さいか・大きいかを数値として返す」というものです。 Sortメソッドの内部では、各要素の比較が行われる度にこのメソッドが呼び出され、戻り値として返される大小関係によって要素の並べ替えが行われることになります。 この形式にあわせて、Accountクラスを比較するメソッドCompareAccountを作成すると次のようになります。
 

        

        
~
#tabpage(codelang=cs,container-title=Accountクラスの比較を行うメソッドのひな形)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
static int CompareAccount(Account x, Account y)
static int CompareAccount(Account x, Account y)
 
{
{
+
  return 0; // xとyを比較した結果を返す
 
}
}
 
}}
}}
 
#tabpage(codelang=vb)
#tabpage(codelang=vb)
 
#code{{
#code{{
 
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
+
  Return 0 ' xとyを比較した結果を返す
 
End Function
End Function
 
}}
}}
 
#tabpage-end
#tabpage-end
139,29 135,29
 
-&var{x}; < &var{y}; (&var{x};が&var{y};より小さい) ならば 負の数
-&var{x}; < &var{y}; (&var{x};が&var{y};より小さい) ならば 負の数
 
-&var{x}; = &var{y}; (&var{x};と&var{y};が等しい) ならば 0
-&var{x}; = &var{y}; (&var{x};と&var{y};が等しい) ならば 0
 

        

        
~
を返すようにします。 このルールに従ってメソッドを実装すると、次のようになります。 正の数・負の数ならなんでもよいので、ここではそれぞれ``1``と``-1``を返しています。
を返すようにします。 このルールに従ってメソッドを実装すると、次のようになります。 正の数・負の数ならなんでもよいので、ここではそれぞれ1と-1を返しています。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
static int CompareAccount(Account x, Account y)
static int CompareAccount(Account x, Account y)
 
{
{
 
  if (x > y)
  if (x > y)
~
    return 1; // xがyより大きい
    return 1;
 
  else if (x < y)
  else if (x < y)
~
    return -1; // xがyより小さい
    return -1;
 
  else // if (x == y)
  else // if (x == y)
~
    return 0; // xとyが等しい
    return 0;
 
}
}
 
}}
}}
 
#tabpage(codelang=vb)
#tabpage(codelang=vb)
 
#code{{
#code{{
 
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
 
  If x > y Then
  If x > y Then
~
    Return 1 ' xがyより大きい
    Return 1
 
  Else If x < y
  Else If x < y
~
    Return -1 ' xがyより小さい
    Return -1
 
  Else ' If x = y
  Else ' If x = y
~
    Return 0 ' xとyが等しい
    Return 0
 
  End If
  End If
 
End Function
End Function
 
}}
}}
174,22 170,22
 
static int CompareAccount(Account x, Account y)
static int CompareAccount(Account x, Account y)
 
{
{
 
  if (x.ID > y.ID)
  if (x.ID > y.ID)
~
    return 1; // xのIDがyのIDより大きい→xはyより大きい
    return 1;
 
  else if (x.ID < y.ID)
  else if (x.ID < y.ID)
~
    return -1; // xのIDがyのIDより小さい→xはyより小さい
    return -1;
 
  else // if (x.ID == y.ID)
  else // if (x.ID == y.ID)
~
    return 0; // xのIDとyのIDが等しい→xはyと等しい
    return 0;
 
}
}
 
}}
}}
 
#tabpage(codelang=vb)
#tabpage(codelang=vb)
 
#code{{
#code{{
 
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
 
  If x.ID > y.ID Then
  If x.ID > y.ID Then
~
    Return 1 ' xのIDがyのIDより大きい→xはyより大きい
    Return 1
 
  Else If x.ID < y.ID
  Else If x.ID < y.ID
~
    Return -1 ' xのIDがyのIDより小さい→xはyより小さい
    Return -1
 
  Else ' If x.ID = y.ID
  Else ' If x.ID = y.ID
~
    Return 0 ' xのIDとyのIDが等しい→xはyと等しい
    Return 0
 
  End If
  End If
 
End Function
End Function
 
}}
}}
214,7 210,6
 
}
}
 

        

        
 
class Sample {
class Sample {
+
  // IDプロパティの値にしたがってAccountクラスの比較を行うメソッド
 
  static int CompareAccount(Account x, Account y)
  static int CompareAccount(Account x, Account y)
 
  {
  {
 
    if (x.ID > y.ID)
    if (x.ID > y.ID)
235,7 230,7
 
      new Account(4, "Eve"),
      new Account(4, "Eve"),
 
    });
    });
 

        

        
~
    // 比較用のメソッドを指定してソート
    // ソート
 
    list.Sort(CompareAccount);
    list.Sort(CompareAccount);
 

        

        
 
    foreach (Account a in list) {
    foreach (Account a in list) {
260,7 255,6
 
End Class
End Class
 

        

        
 
Class Sample
Class Sample
+
  ' IDプロパティの値にしたがってAccountクラスの比較を行うメソッド
 
  Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
  Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
 
    If x.ID > y.ID Then
    If x.ID > y.ID Then
 
      Return 1
      Return 1
280,7 274,7
 
      New Account(4, "Eve") _
      New Account(4, "Eve") _
 
    })
    })
 

        

        
~
    ' 比較用のメソッドを指定してソート
    ' ソート
 
    list.Sort(AddressOf CompareAccount)
    list.Sort(AddressOf CompareAccount)
 

        

        
 
    For Each a As Account In list
    For Each a As Account In list
328,206 322,12
 

        

        
 
なお、ここまでは昇順となるようにしてきましたが、降順にしたければCompareAccountで定義する大小関係を逆にするか、Reverseメソッドを使ってソートした後で逆順にするだけです。
なお、ここまでは昇順となるようにしてきましたが、降順にしたければCompareAccountで定義する大小関係を逆にするか、Reverseメソッドを使ってソートした後で逆順にするだけです。
 

        

        
+
#remarks
+
降順でのソートについては[[programming/netfx/sorting/0_basictypes#SortDescending]]を参照してください。
+
#remarks-end
+

          
+
#remarks
+
文字列の比較やString.Compareメソッドの詳細に関しては[[programming/netfx/string/2_1_comparison#string_comparison]]を参照してください。
+
#remarks-end
+

          
+
***null/Nothingの考慮
+
ソートしたい対象が参照型のコレクションで、かつコレクションに``null``/``Nothing``が含まれる可能性がある場合は、比較を行うメソッドにおいても``null``/``Nothing``だった場合の考慮を行う必要があります。 具体的には次のように``null``/``Nothing``との比較を行うコードを追加します。
+

          
+
#tabpage(codelang=cs,container-title=引数にnullが渡される可能性のある比較メソッドの実装例)
+
#code{{
+
static int CompareAccount(Account x, Account y)
+
{
+
  if (x == y)
+
    return 0; // 同一のインスタンス、またはどちらもnullの場合、xとyは等しいものとする
+
  else if (x == null)
+
    return -1; // xがnull、かつyがnull以外の場合、xはyより小さいものとする
+
  else if (y == null)
+
    return 1; // yがnull、かつxがnull以外の場合、xはyより大きいものとする
+

          
+
  // どちらもnull以外の場合、IDプロパティの値で比較する
+
  if (x.ID > y.ID)
+
    return 1;
+
  else if (x.ID < y.ID)
+
    return -1;
+
  else // if (x.ID == y.ID)
+
    return 0;
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
+
  If x Is y Then
+
    Return 0 ' 同一のインスタンス、またはどちらもNothingの場合、xとyは等しいものとする
+
  Else If x Is Nothing Then
+
    Return -1 ' xがNothing、かつyがNothing以外の場合、xはyより小さいものとする
+
  Else If y Is Nothing Then
+
    Return 1 ' yがNothing、かつxがNothing以外の場合、xはyより大きいものとする
+
  End If
+

          
+
  ' どちらもNothing以外の場合、IDプロパティの値で比較する
+
  If x.ID > y.ID Then
+
    Return 1 ' xのIDがyのIDより大きい→xはyより大きい
+
  Else If x.ID < y.ID
+
    Return -1 ' xのIDがyのIDより小さい→xはyより小さい
+
  Else ' If x.ID = y.ID
+
    Return 0 ' xのIDとyのIDが等しい→xはyと等しい
+
  End If
+
End Function
+
}}
+
#tabpage-end
+

          
+
.NET Frameworkにおける文字列型(Stringクラス)では、''null/Nothingは他のいずれの値よりも小さい''、また''null/Nothing同士は等しい''ものとして比較が行われるため、上記の例もこれに準じた結果を返すようにしています。
+

          
+
以下は上記のように定義した比較メソッドを使って``null``/``Nothing``を含むコレクションをソートする例です。
+

          
+
#tabpage(codelang=cs,container-title=nullを含むコレクションをソートする例)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+

          
+
class Account {
+
  public int ID;
+
  public string Name;
+

          
+
  public Account(int id, string name)
+
  {
+
    this.ID = id;
+
    this.Name = name;
+
  }
+
}
+

          
+
class Sample {
+
  // IDプロパティの値にしたがってAccountクラスの比較を行うメソッド
+
  static int CompareAccount(Account x, Account y)
+
  {
+
    if (x == y)
+
      return 0; // 同一のインスタンス、またはどちらもnullの場合、xとyは等しいものとする
+
    else if (x == null)
+
      return -1; // xがnull、かつyがnull以外の場合、xはyより小さいものとする
+
    else if (y == null)
+
      return 1; // yがnull、かつxがnull以外の場合、xはyより大きいものとする
+

          
+
    // どちらもnull以外の場合、IDプロパティの値で比較する
+
    if (x.ID > y.ID)
+
      return 1;
+
    else if (x.ID < y.ID)
+
      return -1;
+
    else // if (x.ID == y.ID)
+
      return 0;
+
  }
+

          
+
  static void Main()
+
  {
+
    // 要素にnullを含むコレクション
+
    List<Account> list = new List<Account>(new Account[] {
+
      new Account(3, "Bob"),
+
      new Account(1, "Dave"),
+
      new Account(2, "Alice"),
+
      null,
+
      new Account(5, "Charlie"),
+
      null,
+
      new Account(4, "Eve"),
+
    });
+

          
+
    // 比較用のメソッドを指定してソート
+
    list.Sort(CompareAccount);
+

          
+
    foreach (Account a in list) {
+
      if (a == null)
+
        Console.WriteLine("(null)");
+
      else
+
        Console.WriteLine("{0} {1}", a.ID, a.Name);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+

          
+
Class Account
+
  Public ID As Integer
+
  Public Name As String
+

          
+
  Public Sub New(ByVal id As Integer, ByVal name As String)
+
    MyClass.ID = id
+
    MyClass.Name = name
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  ' IDプロパティの値にしたがってAccountクラスの比較を行うメソッド
+
  Shared Function CompareAccount(ByVal x As Account, ByVal y As Account) As Integer
+
    If x Is y Then
+
      Return 0 ' 同一のインスタンス、またはどちらもNothingの場合、xとyは等しいものとする
+
    Else If x Is Nothing Then
+
      Return -1 ' xがNothing、かつyがNothing以外の場合、xはyより小さいものとする
+
    Else If y Is Nothing Then
+
      Return 1 ' yがNothing、かつxがNothing以外の場合、xはyより大きいものとする
+
    End If
+

          
+
    ' どちらもNothing以外の場合、IDプロパティの値で比較する
+
    If x.ID > y.ID Then
+
      Return 1 ' xのIDがyのIDより大きい→xはyより大きい
+
    Else If x.ID < y.ID
+
      Return -1 ' xのIDがyのIDより小さい→xはyより小さい
+
    Else ' If x.ID = y.ID
+
      Return 0 ' xのIDとyのIDが等しい→xはyと等しい
+
    End If
+
  End Function
+

          
+
  Shared Sub Main()
+
    ' 要素にNothingを含むコレクション
+
    Dim list As New List(Of Account)(New Account() { _
+
      New Account(3, "Bob"), _
+
      New Account(1, "Dave"), _
+
      New Account(2, "Alice"), _
+
      Nothing, _
+
      New Account(5, "Charlie"), _
+
      Nothing, _
+
      New Account(4, "Eve") _
+
    })
+

          
+
    ' 比較用のメソッドを指定してソート
+
    list.Sort(AddressOf CompareAccount)
+

          
+
    For Each a As Account In list
+
      If a Is Nothing Then
+
        Console.WriteLine("(Nothing)")
+
      Else
+
        Console.WriteLine("{0} {1}", a.ID, a.Name)
+
      End If
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
(null)
+
(null)
+
1 Dave
+
2 Alice
+
3 Bob
+
4 Eve
+
5 Charlie
+
}}
+

          
+
このように、昇順の場合は``null``/``Nothing``が前の方へ並べ替えられます。 後ろの方へ並べ替えたい場合は、比較メソッドを変更して''null/Nothingは他のいずれの値よりも大きい''と定義することによって実現できます。
+

          
 
**OrderBy
**OrderBy
~
複合型は1つ以上の基本型から構成されるため、複合型のソートで実際にキーとして選ばれるのは単純型のメンバとなります。 また、単純型のソートの場合でも、単に昇順・降順でのソートで十分な場合がほとんどで、独自の並び替え順序が必要になる事はそう多くありません。
複合型は複数の基本型からなるため、多くの場合、複合型のソートで実際にキーとして選ばれるのは単純型のメンバとなります。 また、単純型のソートの場合でも、単に昇順・降順でのソートで十分な場合がほとんどで、独自の並び替え順序が必要になる事は少ないです。
 

        

        
~
OrderByメソッドは、複合型をソートしたい場合でもキーとなるメンバを選択するだけでソートすることが出来、また選択したキーが単純型であれば並べ替え順序を定義する必要もないため、より少ない記述でソートを行うことが出来ます。
OrderByメソッドは、複合型をソートしたい場合でもキーとなるメンバを選択するだけでソートすることが出来、また選択したキーが単純型であれば並べ替え順序を定義する必要もないため、手早くソートを行うことが出来ます。
 

        

        
~
OrderByメソッドの詳細とキーの選択については[[programming/netfx/sorting/0_basictypes#Enumerable.OrderBy_KeySelector]]で解説しているので省略しますが、先に挙げた例をList.SortではなくOrderByメソッドでソートするように変えると、以下のようになります。
[[OrderByメソッドの詳細とキーの選択>programming/netfx/sorting/0_basictypes#Enumerable.OrderBy]]については既に解説しているので省略しますが、先に挙げた例をOrderByメソッドでソートするようにすると、以下のようになります。
 

        

        
 
#tabpage(codelang=cs)
#tabpage(codelang=cs)
 
#code{{
#code{{
615,179 415,18
 
5 Charlie
5 Charlie
 
}}
}}
 

        

        
~
OrderByメソッドの代わりに[[OrderByDescendingメソッド>programming/netfx/sorting/0_basictypes#Enumerable.OrderByDescending]]を使うことにより、降順でソートすることが出来ます。
また、OrderByメソッドの代わりに[[OrderByDescendingメソッド>programming/netfx/sorting/0_basictypes#Enumerable.OrderByDescending]]を使うことで降順にソートすることが出来ます。
+

          
+
***null/Nothingの考慮
+
OrderByメソッドではキーの選択を行ってソートを行います。 コレクション内の要素が``null``/``Nothing``だった場合にはキーとして使う値を参照しようとしてもヌル参照となるため、ソートを継続することができません。 そのため、ソートしたい対象が参照型のコレクションで、かつコレクションに``null``/``Nothing``が含まれる可能性がある場合は、要素が``null``/``Nothing``だった場合の考慮を行う必要があります。
+

          
+
具体的には、次のようにして非nullの要素のみをソートするようにします。
+
+コレクション内の要素のうち、nullの要素とそうでないものを分ける (Whereメソッド)
+
+非nullの要素のみソートを行う (OrderByメソッド)
+
+nullの要素と、ソートした非nullの要素を結合する (Concatメソッド)
+

          
+
以下は上記のようにして``null``/``Nothing``を含むコレクションをソートする例です。
+

          
+
#tabpage(codelang=cs,container-title=nullを含むコレクションをソートする例)
+
#code{{
+
using System;
+
using System.Collections.Generic;
+
using System.Linq;
+

          
+
class Account {
+
  public int ID;
+
  public string Name;
+

          
+
  public Account(int id, string name)
+
  {
+
    this.ID = id;
+
    this.Name = name;
+
  }
+
}
+

          
+
class Sample {
+
  static void Main()
+
  {
+
    // 要素にnullを含むコレクション
+
    List<Account> list = new List<Account>(new Account[] {
+
      new Account(3, "Bob"),
+
      new Account(1, "Dave"),
+
      new Account(2, "Alice"),
+
      null,
+
      new Account(5, "Charlie"),
+
      null,
+
      new Account(4, "Eve"),
+
    });
+

          
+
    // コレクションのうち、nullの要素のみを取得する
+
    IEnumerable<Account> nulls = list.Where(a => a == null);
+

          
+
    // コレクションのうち、非nullの要素のみを取得する
+
    IEnumerable<Account> nonNulls = list.Where(a => a != null);
+

          
+
    // nullの要素と、ソートした非nullの要素を結合する
+
    IEnumerable<Account> sorted = nulls.Concat(nonNulls.OrderBy(a => a.ID));
+

          
+
    foreach (Account a in sorted) {
+
      if (a == null)
+
        Console.WriteLine("(null)");
+
      else
+
        Console.WriteLine("{0} {1}", a.ID, a.Name);
+
    }
+
  }
+
}
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
Imports System
+
Imports System.Collections.Generic
+
Imports System.Linq
+

          
+
Class Account
+
  Public ID As Integer
+
  Public Name As String
+

          
+
  Public Sub New(ByVal id As Integer, ByVal name As String)
+
    MyClass.ID = id
+
    MyClass.Name = name
+
  End Sub
+
End Class
+

          
+
Class Sample
+
  Shared Sub Main()
+
    ' 要素にNothingを含むコレクション
+
    Dim list As New List(Of Account)(New Account() { _
+
      New Account(3, "Bob"), _
+
      New Account(1, "Dave"), _
+
      New Account(2, "Alice"), _
+
      Nothing, _
+
      New Account(5, "Charlie"), _
+
      Nothing, _
+
      New Account(4, "Eve") _
+
    })
+

          
+
    ' コレクションのうち、Nothingの要素のみを取得する
+
    Dim nulls As IEnumerable(Of Account) = list.Where(Function(a) a Is Nothing)
+

          
+
    ' コレクションのうち、非Nothingの要素のみを取得する
+
    Dim nonNulls As IEnumerable(Of Account) = list.Where(Function(a) a IsNot Nothing)
+

          
+
    ' Nothingの要素と、ソートした非Nothingの要素を結合する
+
    Dim sorted As IEnumerable(Of Account) = nulls.Concat(nonNulls.OrderBy(Function(a) a.ID))
+

          
+
    For Each a As Account In sorted
+
      If a Is Nothing Then
+
        Console.WriteLine("(Nothing)")
+
      Else
+
        Console.WriteLine("{0} {1}", a.ID, a.Name)
+
      End If
+
    Next
+
  End Sub
+
End Class
+
}}
+
#tabpage-end
+

          
+
#prompt(実行結果){{
+
(null)
+
(null)
+
1 Dave
+
2 Alice
+
3 Bob
+
4 Eve
+
5 Charlie
+
}}
+

          
+
この例では``null``/``Nothing``を先に結合しているため、``null``/``Nothing``が前の方へ並べ替えられます。 後ろの方へ並べ替えたい場合は、次のように結合順序を逆にすることで実現できます。
+

          
+
#tabpage(codelang=cs,container-title=nullを後ろの方へ並べ替えてソートする例)
+
#code{{
+
// ソートした非nullの要素の後にnullの要素を結合する
+
IEnumerable<Account> sorted = nonNulls.OrderBy(a => a.ID).Concat(nulls);
+
}}
+
#tabpage(codelang=vb)
+
#code{{
+
' ソートした非Nothingの要素の後にNothingの要素を結合する
+
Dim sorted As IEnumerable(Of Account) = nonNulls.OrderBy(Function(a) a.ID).Concat(nulls)
+
}}
+
#tabpage-end
+

          
+

          
 

        

        
 
*複数キーでのソート [#SortWithMultipleKeys]
*複数キーでのソート [#SortWithMultipleKeys]
+
ここでは複数のキーでソートする場合についてを扱います。 たとえば、次のようなデータがあったときに、まずAge(年齢)の若い順に並べ替え、Ageが同じ場合はIDが小さい順に並べ替えたいといった場合です。 言い換えると、Ageを''第一のキー''、IDを''第二のキー''としてソートするための方法です。
+

          
+
#column
+
|*ソート前のデータ
+
|ID|Name|Age|h
+
|3|Bob|16|
+
|1|Dave|25|
+
|2|Alice|16|
+
|5|Charlie|9|
+
|4|Eve|16|
+
|6|Romeo|9|
+
|7|Juliet|25|
+
#column
+
→ソート→
+
#column
+
|*得たい結果
+
|ID|Name|Age|h
+
|5|Charlie|9|
+
|6|Romeo|9|
+
|2|Alice|16|
+
|3|Bob|16|
+
|4|Eve|16|
+
|1|Dave|25|
+
|7|Juliet|25|
+
#column-end
+

          
 
**Sort + Comparison<T>
**Sort + Comparison<T>
 
これまでの例では、並べ替え順序を定義する際に単一のキーのみを用いていましたが、並べ替え順序の定義を拡張することで複数のキーでソートできるようになります。 複数のキーでソートするには、まず第一のキーで比較し、同じだった場合は第二のキーで比較するように並べ替え順序を定義することで実現できます。
これまでの例では、並べ替え順序を定義する際に単一のキーのみを用いていましたが、並べ替え順序の定義を拡張することで複数のキーでソートできるようになります。 複数のキーでソートするには、まず第一のキーで比較し、同じだった場合は第二のキーで比較するように並べ替え順序を定義することで実現できます。
 

        

        
~
まず、一つのキーでソートする場合について振り返ると、比較を行うメソッドでは引数として渡される二つの要素&var{x};と&var{y};について、その大小関係に従って次のような値を返すように実装する必要がありました。
まず、一つのキーでソートする場合について振り返ると、比較を行うメソッドでは引数として渡される二つの要素&var{x};と&var{y};について、その大小関係に従って
 
-&var{x}; > &var{y}; ならば 正の数
-&var{x}; > &var{y}; ならば 正の数
 
-&var{x}; < &var{y}; ならば 負の数
-&var{x}; < &var{y}; ならば 負の数
 
-&var{x}; = &var{y}; ならば 0
-&var{x}; = &var{y}; ならば 0
 

        

        
~
二つのキーでソートする場合は、要素&var{x};と&var{y};についてまず第一のキーで比較を行い、第一のキーが同じだった場合は第二のキーで比較するようにします。 つまり、上記の条件式を拡張して、比較を行うメソッドでは次のような値を返すように実装します。
を返すように実装する必要がありました。 二つのキーでソートする場合は、要素&var{x};と&var{y};についてまず第一のキーで比較を行い、第一のキーが同じだった場合は第二のキーで比較するようにします。 つまり、上記の条件式を拡張して、比較を行うメソッドでは
 
-第一のキーについて &var{x}; > &var{y}; (&var{x.Key1}; > &var{y.Key1};) ならば 正の数
-第一のキーについて &var{x}; > &var{y}; (&var{x.Key1}; > &var{y.Key1};) ならば 正の数
 
-第一のキーについて &var{x}; < &var{y}; (&var{x.Key1}; < &var{y.Key1};) ならば 負の数
-第一のキーについて &var{x}; < &var{y}; (&var{x.Key1}; < &var{y.Key1};) ならば 負の数
 
-第一のキーについて &var{x}; = &var{y}; (&var{x.Key1}; = &var{y.Key1};) ならば さらに第二のキーで比較する
-第一のキーについて &var{x}; = &var{y}; (&var{x.Key1}; = &var{y.Key1};) ならば さらに第二のキーで比較する
795,11 434,11
 
--第二のキーについて &var{x}; < &var{y}; (&var{x.Key2}; < &var{y.Key2};) ならば 負の数
--第二のキーについて &var{x}; < &var{y}; (&var{x.Key2}; < &var{y.Key2};) ならば 負の数
 
--第二のキーについて &var{x}; < &var{y}; (&var{x.Key2}; = &var{y.Key2};) ならば 0
--第二のキーについて &var{x}; < &var{y}; (&var{x.Key2}; = &var{y.Key2};) ならば 0
 

        

        
~
このようにすることで、各要素はまず第一のキーの順に並べ替えられ、第一のキーが同じ要素については第二のキーの順に並べ替えられるようになり、二つのキーでソートされることになります。
を返すように実装します。 これにより、各要素はまず第一のキーの順に並べ替えられ、第一のキーが同じ要素については第二のキーの順に並べ替えられるようになり、二つのキーでソートされることになります。
 

        

        
 
具体例を見ていきます。 次の例では、ID(int)、名前(string)、年齢(int)のフィールドを持つAccountクラスとそれを格納するリストを作成し、ソートしています。 ここでは第一のキーを年齢、第二のキーをIDとし、年齢が若い順、年齢が同じ場合はIDが小さい順にソートしています。
具体例を見ていきます。 次の例では、ID(int)、名前(string)、年齢(int)のフィールドを持つAccountクラスとそれを格納するリストを作成し、ソートしています。 ここでは第一のキーを年齢、第二のキーをIDとし、年齢が若い順、年齢が同じ場合はIDが小さい順にソートしています。
 

        

        
~
#tabpage(codelang=cs,container-title=Sortメソッドで複数キーのソートを行う例)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Collections.Generic;
using System.Collections.Generic;
1061,13 700,13
 
ここまではキーが二つの場合を例示しましたが、さらに多くのキーでソートしたければ、第二のキーで比較して同じなら第三のキー、第三のキーで比較して同じなら第四のキーと、比較処理を次々拡張することで実現できます。
ここまではキーが二つの場合を例示しましたが、さらに多くのキーでソートしたければ、第二のキーで比較して同じなら第三のキー、第三のキーで比較して同じなら第四のキーと、比較処理を次々拡張することで実現できます。
 

        

        
 
**OrderBy + ThenBy [#Enumerable.ThenBy]
**OrderBy + ThenBy [#Enumerable.ThenBy]
~
[[OrderByメソッドのみを使ったソート>programming/netfx/sorting/0_basictypes#Enumerable.OrderBy]]では単一のキーでしかソート出来ませんが、&msdn(netfx,member,System.Core.dll,System.Linq.Enumerable.ThenBy){ThenByメソッド};と組み合わせて使うと、複数のキーでのソートが出来るようになります。
[[既に解説したOrderByメソッドでのソート>programming/netfx/sorting/0_basictypes#Enumerable.OrderBy]]では単一のキーでしかソート出来ませんが、&msdn(netfx,member,System.Core.dll,System.Linq.Enumerable.ThenBy){ThenByメソッド};と組み合わせて使うと、複数のキーでのソートが出来るようになります。 ThenByメソッドは、OrderByメソッドの戻り値である&msdn(netfx,type,System.Linq.IOrderedEnumerable`1){IOrderedEnumerable<TSource>};に対する拡張メソッドであり、OrderByメソッドの結果(単一のキーでソートした結果)に対してThenByメソッドを呼び出すことにより、第二のキーでソートした結果を得ることが出来ます。
 

        

        
~
ThenByメソッドは、OrderByメソッドの戻り値である&msdn(netfx,type,System.Linq.IOrderedEnumerable`1){IOrderedEnumerable<TSource>};に対する拡張メソッドであり、OrderByメソッドの結果(単一のキーでソートした結果)に対してThenByメソッドを呼び出すことにより、第二のキーでソートした結果を得ることが出来ます。
さらにThenByメソッドの戻り値もIOrderedEnumerable<TSource>であるため、ThenByメソッドの戻り値に対してThenByメソッドを呼び出すと、第三のキーでソートした結果を得られるため、キーの数に応じて重ねてThenByメソッドを呼び出すことで複数キーでのソートを行うことが出来ます。
 

        

        
 
早速、具体例を見ていきます。 次の例では、ID(int)、名前(string)、年齢(int)のフィールドを持つAccountクラスとそれを格納するリストを作成し、ソートしています。 ここでは第一のキーを年齢、第二のキーをIDとし、年齢が若い順、年齢が同じ場合はIDが小さい順にソートしています。
早速、具体例を見ていきます。 次の例では、ID(int)、名前(string)、年齢(int)のフィールドを持つAccountクラスとそれを格納するリストを作成し、ソートしています。 ここでは第一のキーを年齢、第二のキーをIDとし、年齢が若い順、年齢が同じ場合はIDが小さい順にソートしています。
 

        

        
~
#tabpage(codelang=cs,container-title=OrderBy+ThenByメソッドで複数キーのソートを行う例)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Collections.Generic;
using System.Collections.Generic;
1100,7 739,6
 
    });
    });
 

        

        
 
    // ソート
    // ソート
+
    // (AgeをキーとしてOrderByにより並べ替え、その結果をさらにIDをキーとしてThenByで並べ替える)
 
    IOrderedEnumerable<Account> sorted = list.OrderBy(a => a.Age).ThenBy(a => a.ID);
    IOrderedEnumerable<Account> sorted = list.OrderBy(a => a.Age).ThenBy(a => a.ID);
 

        

        
 
    foreach (Account a in sorted) {
    foreach (Account a in sorted) {
1139,7 777,6
 
    })
    })
 

        

        
 
    ' ソート
    ' ソート
+
    ' (AgeをキーとしてOrderByにより並べ替え、その結果をさらにIDをキーとしてThenByで並べ替える)
 
    Dim sorted As IOrderedEnumerable(Of Account) = list.OrderBy(Function(a) a.Age).ThenBy(Function(a) a.ID)
    Dim sorted As IOrderedEnumerable(Of Account) = list.OrderBy(Function(a) a.Age).ThenBy(Function(a) a.ID)
 

        

        
 
    For Each a As Account In sorted
    For Each a As Account In sorted
1160,13 797,9
 
7 Juliet   25
7 Juliet   25
 
}}
}}
 

        

        
~
さらに、ThenByメソッドの戻り値もIOrderedEnumerable<TSource>であるため、ThenByメソッドの戻り値に対してThenByメソッドを呼び出すと、第三のキーでソートした結果を得られるようになります。 このように、キーの数に応じてThenByメソッドを続けて呼び出すことで複数キーでのソートを行うことが出来ます。
なお、OrderByメソッドと同様、ThenByメソッドも''遅延実行''されるため、戻り値に対してforeach等で列挙操作を行うことで初めてソートが行われる点に注意してください。
 

        

        
+
OrderByメソッドと同様、ThenByメソッドも''遅延実行''されるため、戻り値に対してforeach等で列挙操作を行うことで初めてソートが行われる点に注意してください。
 

        

        
+
#remarks
+
ソートと遅延実行に関する注意点については[[programming/netfx/sorting/0_basictypes#Enumerable.OrderBy_modify_collection]]を参照してください。
+
#remarks-end
 

        

        
 
*パフォーマンスの比較 [#performance_comparison]
*パフォーマンスの比較 [#performance_comparison]
 
ここではソートを行ういくつかのケースについてパフォーマンスの比較を行った結果を紹介します。 比較メソッドの定義方法やソートするデータの種類等によっては結果が変わるので、ここで紹介している結果はあくまで参考程度のものです。
ここではソートを行ういくつかのケースについてパフォーマンスの比較を行った結果を紹介します。 比較メソッドの定義方法やソートするデータの種類等によっては結果が変わるので、ここで紹介している結果はあくまで参考程度のものです。

programming/netfx/sorting/2_arrays/index.wiki.txt

current previous
16,13 16,13
 
#adunit
#adunit
 

        

        
 
*ジャグ配列のソート [#SortJaggedArray]
*ジャグ配列のソート [#SortJaggedArray]
~
&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};は任意の配列(Arrayクラス)を引数に取るため、ジャグ配列を指定することもできます。 しかし、単にジャグ配列を指定するだけではジャグ配列をソートすることは出来ません。 ジャグ配列をソートしようとすると、InvalidOperationExceptionがスローされます。
&msdn(netfx,member,System.Array.Sort){Array.Sortメソッド};は任意の配列(Arrayクラス)を引数に取るため、ジャグ配列を指定することもできますが、単にジャグ配列を指定するだけではソートすることは出来ません。 ジャグ配列をソートしようとすると、InvalidOperationExceptionがスローされます。
 

        

        
~
これは、ソートの際にジャグ配列内の個々の要素(つまりジャグ配列内の配列同士)を比較しようとするものの、配列同士をそのまま比較することができないことにより例外がスローされるためです。 そこで、[[programming/netfx/sorting/1_compositetypes]]でも解説したように、配列同士の比較を行うメソッドを用意し、それをArray.Sortメソッドに指定することでジャグ配列のソートが出来るようになります。
これは、ソートの際にジャグ配列内の個々の要素、つまりジャグ配列内の配列同士を比較しようとするものの、配列同士をそのまま比較することができないために例外がスローされるためです。 そこで、[[programming/netfx/sorting/1_compositetypes]]でも解説したように、配列同士の比較を行うメソッドを用意し、それをArray.Sortメソッドに指定することでジャグ配列のソートが出来るようになります。
 

        

        
 
早速、次のような2段のジャグ配列(配列の配列)をソートする場合について見ていきます。
早速、次のような2段のジャグ配列(配列の配列)をソートする場合について見ていきます。
 

        

        
~
#tabpage(codelang=cs,container-title=ソートしたいジャグ配列)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
int[][] arr = new int[][] {
int[][] arr = new int[][] {
 
  new int[] {1, 2},
  new int[] {1, 2},
54,27 54,12
 
}}
}}
 
#tabpage-end
#tabpage-end
 

        

        
~
以降、このジャグ配列をソートして次のように並べ替えることを考えます。
まず、比較を行うメソッドでは引数として渡される二つの要素&var{x};と&var{y};について、その大小関係に従って
+

          
+
#prompt{{
+
1, 1, 
+
1, 2, 
+
1, 2, 2, 
+
1, 2, 2, 2, 
+
1, 2, 3, 
+
1, 3, 
+
2, 1, 
+
2, 1, 1, 
+
2, 1, 1, 
+
2, 2, 
+
}}
+

          
+
まず、比較を行うメソッドでは引数として渡される二つの要素&var{x};と&var{y};について、その大小関係に従って次のような値を返すように実装する必要がありました。
 
-&var{x}; > &var{y}; ならば 正の数
-&var{x}; > &var{y}; ならば 正の数
 
-&var{x}; < &var{y}; ならば 負の数
-&var{x}; < &var{y}; ならば 負の数
 
-&var{x}; = &var{y}; ならば 0
-&var{x}; = &var{y}; ならば 0
 

        

        
~
ジャグ配列のソートの場合、要素&var{x};と&var{y};はジャグ配列内の各配列となります。 ジャグ配列の場合はそれぞれ長さの異なる配列を含めることができるため、二つの配列の大小関係を次のように定めることにします。
を返すように実装する必要がありました。 ジャグ配列のソートの場合、要素&var{x};と&var{y};は配列となります。 また、ジャグ配列の場合は異なる長さの配列が含められますが、ここでは二つの配列の大小関係を次のように定めることにします。
 
-二つの配列&var{x};, &var{y};の長さのうち、小さい方を&var{min};とする (&var{min}; = Math.Min(&var{x};.Length, &var{y};.Length))
-二つの配列&var{x};, &var{y};の長さのうち、小さい方を&var{min};とする (&var{min}; = Math.Min(&var{x};.Length, &var{y};.Length))
 
-二つの配列の要素を先頭から&var{min};番目まで一つずつ比較し、&var{n};番目の要素について
-二つの配列の要素を先頭から&var{min};番目まで一つずつ比較し、&var{n};番目の要素について
 
--&var{x};[&var{n};] > &var{y};[&var{n};] ならば &var{x}; > &var{y}; とする (正の数を返す)
--&var{x};[&var{n};] > &var{y};[&var{n};] ならば &var{x}; > &var{y}; とする (正の数を返す)
86,12 71,12
 

        

        
 
この大小関係に従ってジャグ配列をソートするコードを実装すると、次のようになります。
この大小関係に従ってジャグ配列をソートするコードを実装すると、次のようになります。
 

        

        
~
#tabpage(codelang=cs,container-title=2段ジャグ配列のソート)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 

        

        
 
class Sample {
class Sample {
~
  // 長さが異なる二つの配列を比較するメソッド
  // 二つの配列を比較するメソッド
 
  static int CompareArray(int[] x, int[] y)
  static int CompareArray(int[] x, int[] y)
 
  {
  {
 
    int min = Math.Min(x.Length, y.Length);
    int min = Math.Min(x.Length, y.Length);
143,7 128,7
 
Imports System
Imports System
 

        

        
 
Class Sample
Class Sample
~
  ' 長さが異なる二つの配列を比較するメソッド
  ' 二つの配列を比較するメソッド
 
  Shared Function CompareArray(ByVal x As Integer(), ByVal y As Integer()) As Integer
  Shared Function CompareArray(ByVal x As Integer(), ByVal y As Integer()) As Integer
 
    Dim min As Integer = Math.Min(x.Length, y.Length)
    Dim min As Integer = Math.Min(x.Length, y.Length)
 

        

        
207,12 192,10
 

        

        
 
このように、2段のジャグ配列(配列の配列)をソートするには配列同士の大小関係を定義して比較する必要があります。 3段以上のジャグ配列の場合も同様で、例えば3段のジャグ配列(配列の配列の配列)なら2段のジャグ配列(配列の配列)同士の大小関係を定義する必要があります。
このように、2段のジャグ配列(配列の配列)をソートするには配列同士の大小関係を定義して比較する必要があります。 3段以上のジャグ配列の場合も同様で、例えば3段のジャグ配列(配列の配列の配列)なら2段のジャグ配列(配列の配列)同士の大小関係を定義する必要があります。
 

        

        
~
**StructuralComparisons.StructuralComparerを使った矩形ジャグ配列のソート
**StructuralComparisons.StructuralComparerを使ったソート
~
.NET Framework 4より導入された&msdn(netfx,member,System.Collections.StructuralComparisons.StructuralComparer){StructuralComparisons.StructuralComparer};を使うことでもジャグ配列をソートすることが出来ます。 ただし、ソート出来るのはジャグ配列内の各配列の長さが等しい場合(矩形のジャグ配列)に限られます。
.NET Framework 4より導入された&msdn(netfx,member,System.Collections.StructuralComparisons.StructuralComparer){StructuralComparisons.StructuralComparer};を使うことでもジャグ配列をソートすることが出来ます。 ただし、ソート出来るのはジャグ配列内の各配列の長さが等しい場合に限られます。 StructuralComparisonsについたは[[programming/netfx/comparison/2_structural]]で詳しく解説しています。 次の例は、StructuralComparisons.StructuralComparerを使って2段のジャグ配列をソートする例です。
+

          
+
次の例は、StructuralComparisons.StructuralComparerを使って2段のジャグ配列をソートする例です。
 

        

        
~
#tabpage(codelang=cs,container-title=StructuralComparisons.StructuralComparerを使った2段ジャグ配列のソート)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 
using System.Collections;
using System.Collections;
286,14 269,10
 
2, 2, 1, 
2, 2, 1, 
 
}}
}}
 

        

        
+
#remarks
+
StructuralComparisonsについては[[programming/netfx/comparison/2_structural]]で詳しく解説しています。
+
#remarks-end
+

          
 
**ジャグ配列内の各配列のソート
**ジャグ配列内の各配列のソート
 
単にジャグ配列内の各配列をソートしたい(各配列の位置は変えない)のであれば、次の例のように個々の配列に対してSortメソッドを呼び出すだけで出来ます。
単にジャグ配列内の各配列をソートしたい(各配列の位置は変えない)のであれば、次の例のように個々の配列に対してSortメソッドを呼び出すだけで出来ます。
 

        

        
~
#tabpage(codelang=cs,container-title=ジャグ配列内の各配列のソート)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 

        

        
376,11 355,11
 
}}
}}
 

        

        
 
*多次元配列のソート [#SortMultidimensionalArray]
*多次元配列のソート [#SortMultidimensionalArray]
~
[[ジャグ配列の場合>#SortJaggedArray]]と同様、多次元配列もArray.Sortメソッドではソートすることは出来ません。 そもそもArray.Sortメソッドは1次元配列のみのソートしかサポートしていないため、Comparison<T>を指定しても多次元配列ソートすることは出来ず、&msdn(netfx,type,System.RankException){RankException};がスローされます。 そこで、多次元配列をソートするには、一旦すべての要素を1次元配列に展開するか、次元毎に展開してジャグ配列に変換してからソートするなどの方法をとる必要があります。
[[ジャグ配列の場合>#SortJaggedArray]]と同様、多次元配列もArray.Sortメソッドではソートすることは出来ません。 そもそもArray.Sortメソッドは1次元配列のみのソートしかサポートしていないため、Comparison<T>を指定しても多次元配列ソートすることは出来ず、&msdn(netfx,type,System.RankException){RankException};がスローされます。 そこで、多次元配列をソートするには一旦すべての要素を1次元配列に展開するか、次元毎に展開してジャグ配列に変換してからソートするなどの方法をとる必要があります。
 

        

        
~
次の例では、2次元配列を2段のジャグ配列に変換してからソートを行っています。 ジャグ配列のソートについては[[#SortJaggedArray]]で解説したとおりですが、多次元配列から作成したジャグ配列は各次元の配列の長さが同じとなるため、長さが異なる場合を考慮する必要がなくなります。
次の例では、2次元配列を2段のジャグ配列に変換してからソートを行っています。 ジャグ配列のソートについては[[先に解説したとおり>#SortJaggedArray]]ですが、多次元配列から作成したジャグ配列は各次元の配列の長さが同じとなるため、長さが異なる場合を考慮する必要がなくなります。
 

        

        
~
#tabpage(codelang=cs,container-title=2次元配列のソート)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 

        

        
404,7 383,7
 
    return jagged;
    return jagged;
 
  }
  }
 

        

        
~
  // 長さが同じ二つの配列を比較するメソッド
  // 二つの配列を比較するメソッド
 
  static int CompareArray(int[] x, int[] y)
  static int CompareArray(int[] x, int[] y)
 
  {
  {
 
    for (int n = 0; n < x.Length; n++) {
    for (int n = 0; n < x.Length; n++) {
468,7 447,7
 
    Return jagged
    Return jagged
 
  End Function
  End Function
 

        

        
~
  ' 長さが同じ二つの配列を比較するメソッド
  ' 二つの配列を比較するメソッド
 
  Shared Function CompareArray(ByVal x As Integer(), ByVal y As Integer()) As Integer
  Shared Function CompareArray(ByVal x As Integer(), ByVal y As Integer()) As Integer
 
    For n As Integer = 0 To x.Length - 1
    For n As Integer = 0 To x.Length - 1
 
      If x(n) > y(n) Then
      If x(n) > y(n) Then
533,7 512,7
 
|4|"Eve"|
|4|"Eve"|
 
|1|"Dave"|
|1|"Dave"|
 

        

        
~
#tabpage(codelang=cs,container-title=キー・値ペアの配列のソート)
#tabpage(codelang=cs)
 
#code{{
#code{{
 
using System;
using System;
 

        

        

programming/netfx/sorting/9_generic_sort_algorithm/index.wiki.txt

current previous
18,7 18,6
 

        

        
 
#adunit
#adunit
 

        

        
+
*導入
 
ジェネリックなソートアルゴリズムを実装するにあたり、基本形としてint/Integer型の配列をソートして昇順に並べ替えるコードを考えます。 ここでは、実装するソートアルゴリズムとしてクイックソートを使用することにします。
ジェネリックなソートアルゴリズムを実装するにあたり、基本形としてint/Integer型の配列をソートして昇順に並べ替えるコードを考えます。 ここでは、実装するソートアルゴリズムとしてクイックソートを使用することにします。
 

        

        
 
#tabpage(codelang=cs,container-title=クイックソートでint型の配列をソートするコード)
#tabpage(codelang=cs,container-title=クイックソートでint型の配列をソートするコード)
239,10 238,9
 
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 
0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 
 
}}
}}
 

        

        
~
このように、扱うデータの型が変わっているだけで、それ以外のコード(ソートのアルゴリズム部分)は''全く同ものを流用することができます''。 そこで、このソート処理をどのような型に対しても適用できるよう、このQSortメソッドをジェネリックメソッド化することを考えます。
このように、扱うデータの型が変わっているだけで、それ以外のコード(ソートのアルゴリズム部分)は''全く同ものを流用することができます''。
 

        

        
~
*ジェネリックメソッド化
そこで、このソート処理をどのような型に対しても適用できるよう、このQSortメソッドをジェネリックメソッド化することを考えます。 先のコードでソート対象のデータ型として使用していたintもしくはdoubleの個所を、ジェネリック型Tに置き換えることでジェネリックメソッド化します。
+
先のコードでソート対象のデータ型として使用していたdoubleの個所を、ジェネリック型``T``に置き換えることでジェネリックメソッド化することを目指します。
 

        

        
 
#tabpage(codelang=cs,container-title=データ型をパラメータ化したジェネリックなクイックソート)
#tabpage(codelang=cs,container-title=データ型をパラメータ化したジェネリックなクイックソート)
 
#code{{
#code{{
348,11 346,10
 

        

        
 
しかし単純に型をジェネリック型パラメータTに置き換えるだけでは上記のようにコンパイルエラーとなります。
しかし単純に型をジェネリック型パラメータTに置き換えるだけでは上記のようにコンパイルエラーとなります。
 

        

        
~
intやdoubleでは比較演算子``<``が使用できるので、これによって値の大小関係を比較することができます。 一方ジェネリック型パラメータのTは、intやdoubleとなる場合があるだけでなく、比較演算子を持たない型となる場合もあります。 つまり、''型Tは必ずしも比較演算子による大小関係の比較は行えない''ことから、上記のようなコンパイルエラーが発生します。
intやdoubleでは比較演算子``<``が使用できるので、これによって値の大小関係を比較することができます。 一方ジェネリック型パラメータのTは、intやdoubleとなる場合があるだけでなく、比較演算子を持たない型となる場合もあります。 従って、''型Tは必ずしも比較演算子による大小関係の比較は行えない''ことから、上記のようなコンパイルエラーが発生します。
 

        

        
~
そのため、ジェネリック型同士を比較するには比較演算子ではなく他の方法によって比較する必要があります。 このような目的には[[IComparer<T>インターフェイス>programming/netfx/comparison/0_comparison]]を使うことができます。 IComparer<T>インターフェイスのCompareメソッドは[[引数に指定した二つの値の大小関係に応じた戻り値を返す>programming/netfx/comparison/0_comparison#IComparer]]ので、これを比較演算子の代わりに使うことができます。 Array.Sortメソッドなども、IComparer<T>インターフェイスを指定することにより比較演算子を持たない型のソートができるようになっています。
そのため、ジェネリック型どうしを比較するには比較演算子ではなく他の方法によって比較する必要があります。 このような目的には[[IComparer<T>インターフェイス>programming/netfx/comparison/0_comparison]]を使うことができます。 IComparer<T>インターフェイスのCompareメソッドは[[引数に指定した二つの値の大小関係に応じた戻り値を返す>programming/netfx/comparison/0_comparison#IComparer]]ので、これを比較演算子の代わりに使うことができます。 Array.Sortメソッドなども、IComparer<T>インターフェイスを指定することにより比較演算子を持たない型のソートができるようになっています。
 

        

        
+
*IComparer<T>による大小比較のパラメータ化
 
比較演算子の代わりとして使用するIComparer<T>を指定できるようQSortメソッドの引数を増やし、大小関係の比較をIComparer<T>.Compareメソッドで行うように書き換えると次のようになります。
比較演算子の代わりとして使用するIComparer<T>を指定できるようQSortメソッドの引数を増やし、大小関係の比較をIComparer<T>.Compareメソッドで行うように書き換えると次のようになります。
 

        

        
 
#tabpage(codelang=cs,container-title=IComparer<T>.Compareで値を比較するようにしたジェネリックなクイックソート)
#tabpage(codelang=cs,container-title=IComparer<T>.Compareで値を比較するようにしたジェネリックなクイックソート)
467,11 464,7
 

        

        
 
これにより、どのような型でもソートできるジェネリックなメソッドを作成することができました。 またソート順序を''IComparer<T>によって定義・指定できるようになる''ため、デフォルトとは異なる順序でソートしたい場合、例えば文字列をソートする際に大文字小文字の違いを無視する目的で[[StringComparer.OrdinalIgnoreCase>programming/netfx/string/2_2_compareoptions#StringComparer]]を指定する、といった使い方ができるようになります。
これにより、どのような型でもソートできるジェネリックなメソッドを作成することができました。 またソート順序を''IComparer<T>によって定義・指定できるようになる''ため、デフォルトとは異なる順序でソートしたい場合、例えば文字列をソートする際に大文字小文字の違いを無視する目的で[[StringComparer.OrdinalIgnoreCase>programming/netfx/string/2_2_compareoptions#StringComparer]]を指定する、といった使い方ができるようになります。
 

        

        
~
*利便性の向上
一方、比較演算子を持つintやdoubleなどの単純型でも必ずIComparer<T>を指定しなければならなくなりました。 基本型の比較を行うIComparer<T>は&msdn(netfx,member,System.Collections.Generic.Comparer`1.Default){Comparer<T>.Defaultプロパティ};を参照するだけで取得できますが、それでもこれを毎回指定するのは不便です。 そこで、次のようにIComparer<T>を指定しないオーバーロードを増やすことにより、引数の数をジェネリック化する前と同じ数にすることができます。
+
**基本型のIComparer<T>の省略
+
ここまでの実装では、比較演算子を持つintやdoubleなどの基本型の場合でも必ずIComparer<T>を指定する必要があります。 基本型の比較を行うIComparer<T>は&msdn(netfx,member,System.Collections.Generic.Comparer`1.Default){Comparer<T>.Defaultプロパティ};を参照するだけで取得できますが、それでもこれを毎回指定するのは不便です。
+

          
+
そこで、次のようにIComparer<T>を指定しないオーバーロードを増やすことにより、引数の数をジェネリック化する前と同じ数にすることができます。
 

        

        
 
#tabpage(codelang=cs,container-title=IComparer<T>の指定を省略できるようにしたジェネリックなクイックソート)
#tabpage(codelang=cs,container-title=IComparer<T>の指定を省略できるようにしたジェネリックなクイックソート)
 
#code{{
#code{{
621,10 614,7
 
A, a, abc, B, b, c, C, 
A, a, abc, B, b, c, C, 
 
}}
}}
 

        

        
~
**Comparison<T>の導入
また、IComparer<T>.Comparerメソッドと同じように[[Comparison<T>デリゲート>programming/netfx/comparison/0_comparison#Comparison]]を使うオーバーロードも増やせば、インターフェイスを実装する代わりに比較を行うメソッドやラムダ式を指定するだけでよくなるため、より使いやすくすることができます。
+
ソート順序の定義にIComparer<T>を使用する場合、IComparer<T>を実装したクラスを用意する必要があります。 一方、[[Comparison<T>デリゲート>programming/netfx/comparison/0_comparison#Comparison]]を使えば、単にソート順序を定義したメソッドやラムダ式を用意するだけでよくなるため、わざわざクラスを用意する必要がなくなり、実装を簡略化できます。
+

          
+
従って、IComparer<T>.Comparerメソッドと同じようにComparison<T>を使うオーバーロードも増やせば、より使いやすくすることができます。
 

        

        
 
#tabpage(codelang=cs,container-title=Comparison<T>を指定できるようにしたジェネリックなクイックソート)
#tabpage(codelang=cs,container-title=Comparison<T>を指定できるようにしたジェネリックなクイックソート)
 
#code{{
#code{{
781,17 771,13
 
B, a, cc, Aa, abc, ABC, bbbb, 
B, a, cc, Aa, abc, ABC, bbbb, 
 
}}
}}
 

        

        
~
*発展
ここまでで紹介したコードではすでに用意されているIComparer<T>を使用していますが、IComparer<T>を適切に実装することにより、基本型だけでなく独自に作成したクラスや構造体など任意の型のソート順や、複数キーによるソートも定義できるようになります。
+
ここではソートアルゴリズムにクイックソートを使用しましたが、それ以外のソートアルゴリズムの場合もここでの手順と同様にして実装することができます。
+

          
+
ここまでで紹介したコードではComparer<T>.DefaultやStringComparerなどのすでに用意されているIComparer<T>を使用しましたが、IComparer<T>を適切に実装することにより、基本型だけでなく独自に作成したクラスや構造体など任意の型のソート順や、複数キーによるソートも定義できるようになります。
 

        

        
 
#remarks
#remarks
 
IComparer<T>の実装方法などについては[[programming/netfx/comparison/0_comparison]]や[[programming/netfx/sorting/1_compositetypes]]を参照してください。
IComparer<T>の実装方法などについては[[programming/netfx/comparison/0_comparison]]や[[programming/netfx/sorting/1_compositetypes]]を参照してください。
 
#remarks-end
#remarks-end
 

        

        
~
ここではソート対象を配列(``T[]``)としましたが、これを&msdn(netfx,type,System.Collections.Generic.IList`1){IList<T>};にすれば、配列だけでなく&msdn(netfx,type,System.Collections.Generic.List`1){List<T>};を含む幅広いコレクション型のソートに対応できるようになります。
また、ここまでの手順と同様にしてクイックソート以外のソートアルゴリズムも実装することができます。
+

          
 

        

        
 

        

        
 
#navi(..)
#navi(..)

programming/netfx/sorting/index.wiki.txt

current previous
3,7 3,7
 
${smdncms:keywords,ソート,並べ替え,配列,コレクション,Array,Sort,OrderBy}
${smdncms:keywords,ソート,並べ替え,配列,コレクション,Array,Sort,OrderBy}
 
${smdncms:document_versions,codelang=cs,codelang=vb}
${smdncms:document_versions,codelang=cs,codelang=vb}
 

        

        
~
ここでは配列・List・Dictionaryなど様々なコレクションを使ったデータの並べ替え、コレクションのソートについて解説します。 なお、.NET Frameworkに標準で用意されているソート処理の使い方についてを主に扱います。 ソートアルゴリズムを独自に実装する方法については[[programming/netfx/sorting/9_generic_sort_algorithm]]で解説しています。
ここでは配列・List・Dictionaryなど様々なコレクションを使ったデータの並べ替え、コレクションのソートについて解説します。 なお、.NET Frameworkに標準で用意されているソート処理の使い方についてのみを扱い、ここではソートアルゴリズムを独自に実装する方法等については解説しません。
 

        

        
 
#adunit
#adunit