|
注意:配列やコレクション内の要素を検索する方法は、「配列やコレクション内に指定された要素があるか調べ、その位置を知る」に移動しました。
配列やコレクション内の要素を並び替えるここでは、配列やコレクション(以下、「配列」とだけする)内の要素を並び替える方法を説明します。さらに、並び替えの方法を変更する方法も紹介します。 Array.Sortメソッドを使う基本的には、配列の並び替えは、Array.Sortメソッド(.NET Framework 2.0以降では、Array.Sortジェネリックメソッド)で行います。コレクションの並び替えも、Sortメソッドです。 Array.SortはQuickSortアルゴリズムを使用し、計算時間は平均でO(n log n)、最悪でO(n ^ 2)です。ArrayListやList(T)のSortメソッドは、Array.Sortを使用します。 早速ですが、Sortメソッドを使って文字列の配列を並び替える例を示します。 '並び替えを行う配列 Dim ary As String() = New String() {"b", "aaaaa", "cc"} '並び替える Array.Sort(ary) '.NET Framework 2.0以降では次のようにジェネリックメソッドを使えるが、 '上記のようにしても自動的にジェネリックメソッドが使われる 'Array.Sort(Of String)(ary) '並び替えた結果を表示する Console.WriteLine("普通に並び替え") For i As Integer = 0 To ary.Length - 1 Console.WriteLine("{0}: {1}", i, ary(i)) Next //並び替えを行う配列 string[] ary = new string[] { "b", "aaaaa", "cc" }; //並び替える Array.Sort(ary); //.NET Framework 2.0以降では次のようにジェネリックメソッドを使えるが、 //上記のようにしても自動的にジェネリックメソッドが使われる //Array.Sort<string>(ary); //並び替えた結果を表示する Console.WriteLine("普通に並び替え"); for (int i = 0; i < ary.Length; i++) { Console.WriteLine("{0}: {1}", i, ary[i]); } このコードを実行すると、以下のように出力されます。 普通に並び替え 0: aaaaa 1: b 2: cc 次にコレクションを並び替える例を示します。ここでは、ArrayListを並び替えています。結果は配列の場合と同じになります。 'ArrayListに文字列を追加する Dim al As New System.Collections.ArrayList() al.Add("b") al.Add("aaaaa") al.Add("cc") '並び替える al.Sort() '並び替えた結果を表示する Console.WriteLine("普通に並び替え") Dim i As Integer For i = 0 To al.Count - 1 Console.WriteLine("{0}: {1}", i, al(i)) Next //ArrayListに文字列を追加する System.Collections.ArrayList al = new System.Collections.ArrayList(); al.Add("b"); al.Add("aaaaa"); al.Add("cc"); //並び替える al.Sort(); //並び替えた結果を表示する Console.WriteLine("普通に並び替え"); for (int i = 0; i < al.Count; i++) { Console.WriteLine("{0}: {1}", i, al[i]); } 上記のようにSortメソッドを呼び出した時は、要素のCompareToメソッドによってどのように並び替えられるかが決まります。CompareToは2つのオブジェクトの内どちらが大きいかを判断するメソッドで、Sortは要素が小さい順に並び替えます。よってこの方法では、要素のクラスがCompareToメソッドを確実に持つように、IComparableインターフェイスが実装されている必要があります。なおIComparableインターフェイスについては、「自作クラスの配列やコレクションでSortやBinarySearchができるようにする」で詳しく説明しています。 IComparerを使って、並び替え方を変更する上記のようにCompareToメソッドによる並び替えではなく、並び替え方を独自に指定することもできます。そのためにはIComparerインターフェイスを実装したクラスを定義します。IComparerインターフェイスが持つ唯一のメソッドであるCompareメソッドがCompareToメソッドの代わりに2つのオブジェクトを比較します。 この方法であれば、IComparableインターフェイスを実装していない要素でも並び替えることができます。 Compareメソッドでは、1番目のパラメータと2番目のパラメータで渡されるオブジェクトを比較し、1番目の方が2番目より小さいときは負の整数、大きいときは正の整数、同じときは0を返すようにします。 以下に、文字列の長さで並び替えできるようにしたIComparerを実装したクラスの例を示します。 '並び替える方法を定義するクラス 'IComparerインターフェイスを実装する Public Class LengthComparer Implements System.Collections.IComparer 'xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _ Implements System.Collections.IComparer.Compare 'Nothingが最も小さいとする If x Is Nothing AndAlso y Is Nothing Then Return 0 End If If x Is Nothing Then Return -1 End If If y Is Nothing Then Return 1 End If 'String型以外の比較はエラー If Not (TypeOf x Is String) Then Throw New ArgumentException("String型でなければなりません。", "x") ElseIf Not (TypeOf y Is String) Then Throw New ArgumentException("String型でなければなりません。", "y") End If '文字列の長さを比較する Return DirectCast(x, String).Length.CompareTo(DirectCast(y, String).Length) 'または、次のようにもできる 'Return DirectCast(x, String).Length - DirectCast(y, String).Length End Function End Class //並び替える方法を定義するクラス //IComparerインターフェイスを実装する public class LengthComparer : System.Collections.IComparer { //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す public int Compare(object x, object y) { //nullが最も小さいとする if (x == null && y == null) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } //String型以外の比較はエラー if (!(x is string)) { throw new ArgumentException("String型でなければなりません。", "x"); } else if (!(y is string)) { throw new ArgumentException("String型でなければなりません。", "y"); } //文字列の長さを比較する return ((string)x).Length.CompareTo(((string)y).Length); //または、次のようにもできる //return ((string)x).Length - ((string)y).Length; } }
補足:このコードではnullが一番小さいとしていますが、ArgumentExceptionのような例外をスローしてもよいでしょう。
このクラスを使用して並び替えを行うコードを以下に示します。 '並び替えを行う配列を作成 Dim ary As String() = New String() {"b", "aaaaa", "cc"} 'LengthComparerを使って、文字列の長さで並び替える Dim comp As New LengthComparer() Array.Sort(ary, comp) '並び替えた結果を表示する Console.WriteLine("文字列長で並び替え") Dim i As Integer For i = 0 To ary.Length - 1 Console.WriteLine("{0}: {1}", i, ary(i)) Next //並び替えを行う配列を作成 string[] ary = new string[] { "b", "aaaaa", "cc" }; //LengthComparerを使って、文字列の長さで並び替える LengthComparer comp = new LengthComparer(); Array.Sort(ary, comp); //並び替えた結果を表示する Console.WriteLine("文字列長で並び替え"); for (int i = 0; i < ary.Length; i++) { Console.WriteLine("{0}: {1}", i, ary[i]); } この結果、以下のように出力されます。 文字列長で並び替え 0: b 1: cc 2: aaaaa 文字列の配列を大文字小文字を区別しないで並び替えたり、カルチャに依存しないで並べ替えたりするならば、あらかじめ用意されているIComparerを使うこともできます(ただし、.NET Framework 2.0以降)。詳しくは、「文字列の配列やコレクションを並び替える」で説明します。 IComparer(T)ジェネリックインターフェイスを実装する.NET Framework 2.0以降では、IComparer(T)ジェネリックインターフェイスを実装した方が良いでしょう。 先ほどのLengthComparerクラスにIComparer(T)ジェネリックインターフェイスも実装した例を示します。 '並び替える方法を定義するクラス 'IComparer(T)ジェネリックインターフェイスを実装する Public Class LengthComparer Implements System.Collections.IComparer Implements System.Collections.Generic.IComparer(Of String) 'xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す Public Function Compare(ByVal x As String, ByVal y As String) As Integer _ Implements System.Collections.Generic.IComparer(Of String).Compare 'Nothingが最も小さいとする If x Is Nothing AndAlso y Is Nothing Then Return 0 End If If x Is Nothing Then Return -1 End If If y Is Nothing Then Return 1 End If '文字列の長さを比較する Return x.Length.CompareTo(y.Length) End Function Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer _ Implements System.Collections.IComparer.Compare 'Nothingが最も小さいとする If x Is Nothing AndAlso y Is Nothing Then Return 0 End If If x Is Nothing Then Return -1 End If If y Is Nothing Then Return 1 End If 'String型以外の比較はエラー If Not (TypeOf x Is String) Then Throw New ArgumentException("String型でなければなりません。", "x") ElseIf Not (TypeOf y Is String) Then Throw New ArgumentException("String型でなければなりません。", "y") End If Return Me.Compare(DirectCast(x, String), DirectCast(y, String).Length) End Function End Class //並び替える方法を定義するクラス //IComparer(T)ジェネリックインターフェイスを実装する public class LengthComparer : System.Collections.IComparer, System.Collections.Generic.IComparer<string> { //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す public int Compare(string x, string y) { //nullが最も小さいとする if (x == null && y == null) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } //文字列の長さを比較する return x.Length.CompareTo(y.Length); } public int Compare(object x, object y) { //nullが最も小さいとする if (x == null && y == null) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } //String型以外の比較はエラー if (!(x is string)) { throw new ArgumentException("String型でなければなりません。", "x"); } else if (!(y is string)) { throw new ArgumentException("String型でなければなりません。", "y"); } return this.Compare((string)x, (string)y); } } Comparison(T)ジェネリックデリゲートこれは、IComparerインターフェイスとはまた別の方法です。.NET Framework 2.0からは、Comparison(T)ジェネリックデリゲートを使って並び替え方を変更することもできます。この方法ならば、IComparerインターフェイスを実装したクラスを作成する手間が省けます。ただし、ジェネリックではないコレクションではこの方法は使えません。 以下にこの方法による例を示します。まず次のようなメソッドを作成し、並び替え方を指定します。やっていることは先のCompareメソッドと同じです。 'xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す Public Shared Function CompareByLength( _ ByVal x As String, ByVal y As String) As Integer 'Nothingが最も小さいとする If x Is Nothing AndAlso y Is Nothing Then Return 0 End If If x Is Nothing Then Return -1 End If If y Is Nothing Then Return 1 End If '文字列の長さを比較する Return x.Length - y.Length End Function //xがyより小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返す public static int CompareByLength(string x, string y) { //nullが最も小さいとする if (x == null && y == null) { return 0; } if (x == null) { return -1; } if (y == null) { return 1; } //文字列の長さを比較する return x.Length - y.Length; } このメソッドを使って並び替えを行うには、次のようにします。 '並び替えを行う配列を作成 Dim ary As String() = New String() {"b", "aaaaa", "cc"} '並び替えを行う Array.Sort(ary, AddressOf CompareByLength) '並び替えた結果を表示する Console.WriteLine("Comparison(T)で並び替え") For i As Integer = 0 To ary.Length - 1 Console.WriteLine("{0}: {1}", i, ary(i)) Next //並び替えを行う配列を作成 string[] ary = new string[] { "b", "aaaaa", "cc" }; //並び替えを行う Array.Sort(ary, CompareByLength); //並び替えた結果を表示する Console.WriteLine("Comparison(T)で並び替え"); for (int i = 0; i < ary.Length; i++) { Console.WriteLine("{0}: {1}", i, ary[i]); } 次に、C#の匿名メソッドを使ってもっと簡単に記述した方法を示します。残念ながらVB.NETでは匿名メソッドを使えません。 //並び替えを行う配列を作成 string[] ary = new string[] { "b", "aaaaa", "cc" }; //並び替えを行う Array.Sort(ary, delegate(string x, string y) { return x.Length.CompareTo(y.Length); }); Visual Studio 2008(VB9、C#3.0)以降では、次のようにラムダ式を使うこともできます。 '並び替えを行う配列を作成 Dim ary As String() = New String() {"b", "aaaaa", "cc"} '並び替えを行う Array.Sort(ary, _ Function(x As String, y As String) x.Length.CompareTo(y.Length)) //並び替えを行う配列を作成 string[] ary = new string[] { "b", "aaaaa", "cc" }; //並び替えを行う Array.Sort(ary, (x, y) => x.Length.CompareTo(y.Length)); LINQを使う.NET Framework 3.5からは、LINQ(統合言語クエリ、Language-Integrated Query)によって並び替えることもできます。この方法では、並び替える配列はそのままにして、並び替えられた新しい配列が返されます。なおLINQを使用するには、参照設定に「System.Core.dll」を追加する必要があります。 LINQによる並べ替えの詳細はMSDNの「Language-Integrated Query (LINQ) データの並べ替え」などに任せるとして、ここでは簡単な例のみを示します。 まずはLINQで普通に配列を並び替える例です。 'Imports System.Linq 'がソースファイルの一番上に書かれているものとする '並び替えを行う配列 Dim strs As String() = New String() {"b", "aaaaa", "cc"} 'クエリの作成 Dim query = From str In strs _ Order By str _ Select str 'または、 'Dim query As System.Collections.Generic.IEnumerable(Of String) = _ ' From str In strs _ ' Order By str _ ' Select str 'または、 'Dim query = strs.OrderBy(Function(str) str) 'クエリを実行し、配列を作成 Dim sortedStrs As String() = query.ToArray() '並び替えた結果を表示する Console.WriteLine("LINQで並び替え") For i As Integer = 0 To sortedStrs.Length - 1 Console.WriteLine("{0}: {1}", i, sortedStrs(i)) Next 'Imports System.Linq 'がソースファイルの一番上に書かれているものとする //並び替えを行う配列 string[] strs = new string[] { "b", "aaaaa", "cc" }; //クエリの作成 var query = from str in strs orderby str select str; //または、 //System.Collections.Generic.IEnumerable<string> query = // from str in strs // orderby str // select str; //または、 //var query = strs.OrderBy(str => str); //クエリを実行し、配列を作成 string[] sortedStrs = query.ToArray(); //並び替えた結果を表示する Console.WriteLine("LINQで並び替え"); for (int i = 0; i < sortedStrs.Length; i++) { Console.WriteLine("{0}: {1}", i, sortedStrs[i]); } 次に、文字列の配列を文字列長順に並び替える例を示します。 'Imports System.Linq 'がソースファイルの一番上に書かれているものとする '並び替えを行う配列 Dim strs As String() = New String() {"b", "aaaaa", "cc"} 'Lengthプロパティの大きさで並び替える Dim query = From str In strs _ Order By str.Length _ Select str 'または、 'Dim query = strs.OrderBy(Function(str) str.Length)c 'クエリを実行し、配列を作成 Dim sortedStrs As String() = query.ToArray() //using System.Linq; //がソースファイルの一番上に書かれているものとする //並び替えを行う配列 string[] strs = new string[] { "b", "aaaaa", "cc" }; //Lengthプロパティの大きさで並び替える var query = from str in strs orderby str.Length select str; //または、 //var query = strs.OrderBy(str => str.Length); //クエリを実行し、配列を作成 string[] sortedStrs = query.ToArray(); 文字列の長さで並び替え、同じ長さならば普通の文字列の大きさで並び替えるには、次のようにします。 'Imports System.Linq 'がソースファイルの一番上に書かれているものとする '並び替えを行う配列 Dim strs As String() = New String() {"d", "aaaaa", "cc", "b"} '文字列の長さで並び替え、同じ長さならば普通の並び替え Dim query = From str In strs _ Order By str.Length, str _ Select str 'または、 'Dim query = strs.OrderBy(Function(str) str.Length).ThenBy(Function(str) str) 'クエリを実行し、配列を作成 Dim sortedStrs As String() = query.ToArray() '結果を表示 Console.WriteLine(String.Join(", ", sortedStrs)) 'b, d, cc, aaaaa //using System.Linq; //がソースファイルの一番上に書かれているものとする //並び替えを行う配列 string[] strs = new string[] { "d", "aaaaa", "cc" ,"b" }; //文字列の長さで並び替え、同じ長さならば普通の並び替え var query = from str in strs orderby str.Length, str select str; //または、 //var query = strs.OrderBy(str => str.Length).ThenBy(s => s); //クエリを実行し、配列を作成 string[] sortedStrs = query.ToArray(); //結果を表示 Console.WriteLine(string.Join(", ", sortedStrs)); //b, d, cc, aaaaa 文字列の配列を文字列の長さの降順で並び替えるには、次のようにします。 'Imports System.Linq 'がソースファイルの一番上に書かれているものとする '並び替えを行う配列 Dim strs As String() = New String() {"b", "aaaaa", "cc"} 'Lengthプロパティの大きさで降順に並び替える Dim query = From str In strs _ Order By str.Length Descending _ Select str 'または、 'Dim query = strs.OrderByDescending(Function(str) str.Length) 'クエリを実行し、配列を作成 Dim sortedStrs As String() = query.ToArray() //using System.Linq; //がソースファイルの一番上に書かれているものとする //並び替えを行う配列 string[] strs = new string[] { "b", "aaaaa", "cc" }; //Lengthプロパティの大きさで降順に並び替える var query = from str in strs orderby str.Length descending select str; //または、 //var query = strs.OrderByDescending(str => str.Length); //クエリを実行し、配列を作成 string[] sortedStrs = query.ToArray(); 指定した方法で並び替える、特殊な方法配列やコレクションを並び替える方法は、今まで紹介したものが一般的です。ここからは、指定した方法で並び替える方法として考えられるものをさらに幾つか紹介します。 SortedListクラスを使うSortedListクラスはキーと値のペアのコレクションで、キーで並び替えられます。キーの並び替え方は、コンストラクタでIComparerを指定することによって変えることもできます。 .NET Framework 2.0以降では、ジェネリックバージョンの「System.Collections.Generic.SortedList(TKey, TValue)」を使用できます。 SortedListでは、Addメソッドなどで要素を加えた時に並び替えが行われるようです。その時の操作はO(n)で、要素が末尾に追加されるときはO(log n)です。 以下の例では、SortedListクラスのキーに文字列の長さを指定することで、文字列の長さで並び替えられるようにしています。 '並び替えを行う配列 Dim ary As String() = New String() {"b", "aaaaa", "cc"} 'SortedListを作成 Dim sl As New System.Collections.SortedList() '文字列の長さをキーとしてコレクションに追加する Dim s As String For Each s In ary sl.Add(s.Length, s) Next '並び替えた結果を表示する Console.WriteLine("SortedListで並び替え") Dim i As Integer For i = 0 To sl.Count - 1 Console.WriteLine("{0}: {1}", i, sl.GetByIndex(i)) Next //並び替えを行う配列 string[] ary = new string[] { "b", "aaaaa", "cc" }; //SortedListを作成 System.Collections.SortedList sl = new System.Collections.SortedList(); //文字列の長さをキーとしてコレクションに追加する foreach (string s in ary) { sl.Add(s.Length, s); } //並び替えた結果を表示する Console.WriteLine("SortedListで並び替え"); for (int i = 0; i < sl.Count; i++) { Console.WriteLine("{0}: {1}", i, sl.GetByIndex(i)); } Array.Sort(Array, Array)メソッドを使うArray.Sort(Array, Array)メソッドを使うと、一方の配列に基づいて、もう一方の配列を並び替えることができます。この方法でも、IComparerによって並び替え方を変更することができます。 .NET Framework 2.0以降では、適切なジェネリックメソッドが自動的に呼び出されます。 以下にArray.Sort(Array, Array)メソッドを使って文字列の長さで並び替えを行う例を示します。 '並び替えを行う配列 Dim ary As String() = New String() {"b", "aaaaa", "cc"} '並び替えの対象となる配列を作成 Dim keys As Integer() = New Integer(ary.Length - 1) {} Dim i As Integer For i = 0 To keys.Length - 1 '文字列の長さを格納する keys(i) = ary(i).Length Next '並び替えを行う Array.Sort(keys, ary) '並び替えた結果を表示する Console.WriteLine("Array.Sort(Array, Array)で並び替え") For i = 0 To ary.Length - 1 Console.WriteLine("{0}: {1}", i, ary(i)) Next //並び替えを行う配列 string[] ary = new string[] { "b", "aaaaa", "cc" }; //並び替えの対象となる配列を作成 int[] keys = new int[ary.Length]; for (int i = 0; i < keys.Length; i++) { //文字列の長さを格納する keys[i] = ary[i].Length; } //並び替えを行う Array.Sort(keys, ary); //並び替えた結果を表示する Console.WriteLine("Array.Sort(Array, Array)で並び替え"); for (int i = 0; i < ary.Length; i++) { Console.WriteLine("{0}: {1}", i, ary[i]); } どの方法が一番早いかArray.Sortメソッド、List(T).Sortメソッド、LINQの3つの並び替え方法について、ベンチマークの結果が「Sort String Array」で紹介されています。これによると、List(T).Sortメソッドが若干速く、要素数が多いときはLINQが遅いようですが、3つの方法の速さの違いはそれほどないようです。ただしこのベンチマークでは、Sortメソッドは配列やコレクションのコピーを作成してから並び替えているため、コピーを作成しないで並び替えるのであれば、LINQとの差はもっと出るかもしれません。 文字列の並び替え文字列の配列やコレクションを並び替える方法については、「文字列の配列やコレクションを並び替える」でさらに補足しています。そちらでは、大文字、小文字を区別しない並び替えや、カルチャに依存しない並び替えなどについて説明しています。
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。 |
|
Copyright(C) DOBON!. All rights reserved.
|