DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

DOBON.NET

注意:配列やコレクション内の要素を方法を指定して検索する方法は、「配列やコレクション内の要素を方法を指定して検索する」に移動しました。

配列やコレクション内の要素を並び替える

配列の並び替えは、Array.Sortメソッドで行います。コレクションの並び替えは、Sortメソッドで行います。

以下の例では、フォームにボタンコントロール"Button1"があるものとして、これをクリックした時にArrayListの並び替えを行なっています。

[VB.NET]
'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button1.Click
    '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
End Sub
[C#]
//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //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]);
    }
}

上記の結果、以下のように出力されます。

普通に並び替え
0: aaaaa
1: b
2: cc

配列の並び替えは、以下のようになります。

[VB.NET]
'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button1.Click
    '並び替えを行う配列を作成
    Dim strs() As String = {"b", "aaaaa", "cc"}

    '普通に並び替える
    Array.Sort(strs)

    '並び替えた結果を表示する
    Console.WriteLine("普通に並び替え")
    Dim i As Integer
    For i = 0 To strs.Length - 1
        Console.WriteLine("{0}: {1}", i, strs(i))
    Next i
End Sub
[C#]
//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //並び替えを行う配列を作成
    string[] strs = new string[] { "b", "aaaaa", "cc" };

    //普通に並び替える
    Array.Sort(strs);

    //並び替えた結果を表示する
    Console.WriteLine("普通に並び替え");
    for (int i = 0; i < strs.Length; i++)
    {
        Console.WriteLine("{0}: {1}", i, strs[i]);
    }
}

なお、.NET Framework 2.0以降では、Array.Sortジェネリックメソッドを使って、
Array.Sort<string>(strs)
のようにしてください。

このように並べ替え方を指定せずに、既定の方法で並べ替えを行うには、要素のクラスにIComparableインターフェイスが実装されている必要があります。既定の並び替えでは、IComparableインターフェイスのCompareToメソッドを使って大小が比較され、並び替えが行われます。

このようにIComparableインターフェイスを実装して並び替えの方法を指定する方法は「自作クラスの配列やコレクションでSortやBinarySearchを行う」で紹介しています。ここでは、それ以外のやり方で並び替え方を変更する方法を紹介します。

配列やコレクション内の要素を方法を指定して並び替える

IComparerインターフェイス

ここでは、文字列の長さが短い順番に並び替えてみます。ここではまず、IComparerインターフェイスを実装したクラス"LengthComparer"を定義し、Compareメソッドで2つのオブジェクトの比較方法を指定します。

Compareメソッドでは、1番目のパラメータと2番目のパラメータで渡されるデータを比較し、1番目の方が2番目より小さいときはマイナスの数、大きいときはプラスの数、同じときは0を返すようにします。

[VB.NET]
'並び替える方法を定義するクラス
'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 And 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) OrElse (Not TypeOf y Is String) Then
            Throw New ArgumentException()
        End If

        '文字列の長さを比較する
        Return CStr(x).Length - CStr(y).Length
        'または、次のような方法もある
        'Return CStr(x).Length.CompareTo(CStr(y).Length)
    End Function
End Class

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button1.Click
    'ArrayListに文字列を追加する
    Dim al As New System.Collections.ArrayList()
    al.Add("b")
    al.Add("aaaaa")
    al.Add("cc")

    '文字列の長さで並び替える
    Dim comp As New LengthComparer()
    al.Sort(comp)

    '並び替えた結果を表示する
    Console.WriteLine("文字列長で並び替え")
    Dim i As Integer
    For i = 0 To al.Count - 1
        Console.WriteLine("{0}: {1}", i, al(i))
    Next i
End Sub
[C#]
//並び替える方法を定義するクラス
//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) || !(y is string))
            throw new ArgumentException();

        //文字列の長さを比較する
        return ((string)x).Length - ((string)y).Length;
        //または、次のような方法もある
        //return ((string)x).Length.CompareTo(((string)y).Length);
    }
}

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, System.EventArgs e)
{
    //ArrayListに文字列を追加する
    System.Collections.ArrayList al = new System.Collections.ArrayList();
    al.Add("b");
    al.Add("aaaaa");
    al.Add("cc");

    //文字列の長さで並び替える
    LengthComparer comp = new LengthComparer();
    al.Sort(comp);

    //並び替えた結果を表示する
    Console.WriteLine("文字列長で並び替え");
    for (int i = 0; i < al.Count; i++)
    {
        Console.WriteLine("{0}: {1}", i, al[i]);
    }
}

今度は以下のように出力されます。

文字列長で並び替え
0: b
1: cc
2: aaaaa

上記はコレクションの例ですが、配列でも同様に、
Array.Sort(ary, comp)
のようにして並び替えができます。

IComparable(T)ジェネリックインターフェイス

.NET Framework 2.0以降では、IComparableインターフェイスに加えて、IComparable(T)ジェネリックインターフェイスも実装した方が良いでしょう。そして並び替えは、ジェネリックコレクションのSortメソッドなどで行います。

先のLengthComparerクラスにIComparable(T)ジェネリックインターフェイスも実装したクラスは、以下のようになります。

[VB.NET]
'並び替える方法を定義するクラス
'IComparable(T)ジェネリックインターフェイスを実装する
Public Class LengthComparer
    Implements System.Collections.IComparer,  _
        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 - 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) OrElse (Not TypeOf y Is String) Then
            Throw New ArgumentException()
        End If

        Return Me.Compare(CStr(x), CStr(y))
    End Function
End Class
[C#]
//並び替える方法を定義するクラス
//IComparable(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 - 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) || !(y is string))
            throw new ArgumentException();

        return this.Compare((string)x, (string)y);
    }
}

SortedListクラス

SortedListクラスはキーと値のペアのコレクションで、キーで並び替えられます。キーの並び替え方は、IComparerによって変えることもできます。

.NET Framework 2.0以降では、ジェネリックバージョンの「System.Collections.Generic.SortedList(TKey, TValue)」または「SortedDictionary(TKey, TValue)」を使った方が良いでしょう。

以下の例では、SortedListクラスのキーに文字列の長さを指定することで、文字列の長さで並び替えられるようにしています。

[VB.NET]
Dim strs() As String = {"b", "aaaaa", "cc"}

'SortedListを作成
Dim sl As New System.Collections.SortedList()
'文字列の長さをキーとしてコレクションに追加する
Dim s As String
For Each s In strs
    sl.Add(s.Length, s)
Next s

'並び替えた結果を表示する
Console.WriteLine("SortedListで並び替え")
Dim i As Integer
For i = 0 To sl.Count - 1
    Console.WriteLine("{0}: {1}", i, sl.GetByIndex(i))
Next i
[C#]
string[] strs = new string[] { "b", "aaaaa", "cc" };

//SortedListを作成
System.Collections.SortedList sl = new System.Collections.SortedList();
//文字列の長さをキーとしてコレクションに追加する
foreach (string s in strs)
{
    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以降では、ジェネリックメソッド「Sort(TKey, TValue)(TKey[], TValue[])」を使った方が良いでしょう。

以下にArray.Sort(Array, Array)メソッドを使って並び替えを行う例を示します。

[VB.NET]
'並び替えを行う配列
Dim strs() As String = {"b", "aaaaa", "cc"}

'並び替えの対象となる配列を作成
Dim keys(strs.Length - 1) As Integer
Dim i As Integer
For i = 0 To keys.Length - 1
    '文字列の長さを格納する
    keys(i) = strs(i).Length
Next i

'並び替えを行う
Array.Sort(keys, strs)

'並び替えた結果を表示する
Console.WriteLine("Array.Sort(Array, Array)で並び替え")
For i = 0 To strs.Length - 1
    Console.WriteLine("{0}: {1}", i, strs(i))
Next i
[C#]
//並び替えを行う配列
string[] strs = new string[] { "b", "aaaaa", "cc" };

//並び替えの対象となる配列を作成
int[] keys = new int[strs.Length];
for (int i = 0; i < keys.Length; i++)
{
    //文字列の長さを格納する
    keys[i] = strs[i].Length;
}

//並び替えを行う
Array.Sort(keys, strs);

//並び替えた結果を表示する
Console.WriteLine("Array.Sort(Array, Array)で並び替え");
for (int i = 0; i < strs.Length; i++)
{
    Console.WriteLine("{0}: {1}", i, strs[i]);
}

Comparison(T)ジェネリックデリゲート

.NET Framework 2.0からは、Comparison(T)ジェネリックデリゲートを使って並び替え方を変更することもできます。ただし、ジェネリックではないコレクションではこの方法は使えません。

以下にこの方法による例を示します。CompareByLengthメソッドで並び替え方を指定しています。

[VB.NET]
'xがyより小さいときはマイナスの数、大きいときはプラスの数、
'同じときは0を返す
Public 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

'Button1のClickイベントハンドラ
Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles Button1.Click
    'List(Of string)に文字列を追加する
    Dim sl As New List(Of String)
    sl.Add("b")
    sl.Add("aaaaa")
    sl.Add("cc")

    '並び替えを行う
    sl.Sort(AddressOf CompareByLength)

    '並び替えた結果を表示する
    Console.WriteLine("Comparison(T)で並び替え")
    Dim i As Integer
    For i = 0 To sl.Count - 1
        Console.WriteLine("{0}: {1}", i, sl(i))
    Next i
End Sub
[C#]
//xがyより小さいときはマイナスの数、大きいときはプラスの数、
//同じときは0を返す
public 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;
}

//Button1のClickイベントハンドラ
private void Button1_Click(object sender, EventArgs e)
{
    //List<string>に文字列を追加する
    List<string> sl = new List<string>();
    sl.Add("b");
    sl.Add("aaaaa");
    sl.Add("cc");

    //並び替えを行う
    sl.Sort(CompareByLength);

    //並び替えた結果を表示する
    Console.WriteLine("Comparison(T)で並び替え");
    for (int i = 0; i < sl.Count; i++)
    {
        Console.WriteLine("{0}: {1}", i, sl[i]);
    }
}

LINQ

.NET Framework 3.5からは、LINQ(統合言語クエリ、Language-Integrated Query)によって並び替えることもできます。なおLINQを使用するには、参照設定に「System.Core.dll」が追加されている必要があります。

LINQにより文字列の配列を文字列長順に並び替える例を示します。

[VB.NET]
'Imports System.Linq
'がソースファイルの一番上に書かれているものとする 

'並び替えを行う配列 
Dim strs As String() = New String() {"b", "aaaaa", "cc"}

'クエリの作成
Dim query As IEnumerable(Of String) = From str In strs _
                                      Order By str.Length _
                                      Select str
'または、
'Dim query = From str In strs _
'            Order By str.Length _
'            Select 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
[C#]
//using System.Linq;
//がソースファイルの一番上に書かれているものとする

//並び替えを行う配列
string[] strs = new string[] { "b", "aaaaa", "cc" };

//クエリの作成
IEnumerable<string> query = from str in strs
                            orderby str.Length
                            select str;
//または、
//var query = from str in strs
//            orderby str.Length
//            select str;

//クエリを実行し、配列を作成
string[] sortedStrs = query.ToArray();

//並び替えた結果を表示する
Console.WriteLine("LINQで並び替え");
for (int i = 0; i < sortedStrs.Length; i++)
{
    Console.WriteLine("{0}: {1}", i, sortedStrs[i]);
}

LINQによる並べ替えの詳細は、MSDNの「Language-Integrated Query (LINQ) データの並べ替え」をご覧ください。

どの方法が一番早いか

Array.Sortメソッド、List(T).Sortメソッド、LINQの3つの並び替え方法について、ベンチマークの結果が「Sort String Array」で紹介されています。これによると、List(T).Sortメソッドが若干速く、要素数が多いときはLINQが遅いようですが、3つの方法の速さの違いはそれほどないようです。

文字列の並び替え

文字列の配列やコレクションを並び替える方法については、こちらでさらに補足しています。そちらでは、大文字、小文字を区別しない並び替えや、カルチャに依存しない並び替えなどについて説明しています。

  • 履歴:
  • 2008/7/15 大幅に書き換える。検索の方法を別のページに移す。IComparable(T)ジェネリックインターフェイス、SortedListクラス、Array.Sort(Array, Array)メソッド、Comparison(T)ジェネリックデリゲートの説明を追加。
  • 2008/8/27 LINQによる方法を追記。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • 「???を参照に追加します」の意味が分からないという方は、こちらをご覧ください。