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

注意:配列やコレクション内の要素を検索する方法は、「配列やコレクション内に指定された要素があるか調べ、その位置を知る」に移動しました。

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

ここでは、配列やコレクション内の要素を並び替える方法を説明します。さらに、並び替えの方法を変更する方法も紹介します。

Array.Sortメソッドを使う

基本的には、配列の並び替えは、Array.Sortメソッド(.NET Framework 2.0以降では、Array.Sortジェネリックメソッド)で行います。コレクションの並び替えも、Sortメソッドです。

Array.Sortは、.NET Framework 4.5以降では、イントロソート(Introsort)アルゴリズムを使用し、計算時間は最悪でO(n log n)です。.NET Framework 4.0以前では、クイックソート(quicksort)アルゴリズムを使用し、計算時間は平均でO(n log n)、最悪でO(n ^ 2)です。

ArrayListやList<T>のSortメソッドも、内部ではArray.Sortを使用しています。

早速ですが、Sortメソッドを使って文字列の配列を並び替える例を示します。

VB.NET
コードを隠すコードを選択
'並び替えを行う配列
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

'並び替える
Array.Sort(ary)
'.NET Framework 2.0以降では次のようにジェネリックメソッドを使えるが、
'上記のようにしても自動的にジェネリックメソッドが使われる
'Array.Sort(Of String)(ary)

'aryは { "aaaaa", "b", "cc" } となる
C#
コードを隠すコードを選択
//並び替えを行う配列
string[] ary = new string[] { "b", "aaaaa", "cc" };

//並び替える
Array.Sort(ary);
//.NET Framework 2.0以降では次のようにジェネリックメソッドを使えるが、
//上記のようにしても自動的にジェネリックメソッドが使われる
//Array.Sort<string>(ary);

//aryは { "aaaaa", "b", "cc" } となる

次にコレクションを並び替える例を示します。ここでは、ArrayListを並び替えています。

VB.NET
コードを隠すコードを選択
'並び替えるArrayListを作成する
Dim al As New System.Collections.ArrayList()
al.Add("b")
al.Add("aaaaa")
al.Add("cc")

'並び替える
al.Sort()

'alは { "aaaaa", "b", "cc" } となる
C#
コードを隠すコードを選択
//並び替えるArrayListを作成する
System.Collections.ArrayList al = new System.Collections.ArrayList();
al.Add("b");
al.Add("aaaaa");
al.Add("cc");

//並び替える
al.Sort();

//alは { "aaaaa", "b", "cc" } となる

デフォルトの並び替え方について

上記のように何もしないでSortメソッドを呼び出した時は、要素のCompareToメソッドによってどのように並び替えられるかが決まります。CompareToは2つのオブジェクトの内どちらが大きいかを判断するメソッドで、Sortは要素が小さい順に並び替えます。よってこの方法では、要素の型がメンバとしてCompareToメソッドを持っていなければならず、そのためにIComparableインターフェイスが実装されている必要があります。IComparableインターフェイスを実装する方法については「自作クラスの配列やコレクションでSortやBinarySearchができるようにする」で詳しく説明していますので、そちらをご覧ください。

IComparerを使って、並び替え方を変更する

上記のようにCompareToメソッドによる並び替えではなく、並び替え方を独自に指定することもできます。そのためにはIComparerインターフェイスを実装したクラスを定義します。IComparerインターフェイスのメンバにはCompareメソッドしかなく、IComparable.CompareToメソッドの代わりにこのメソッドで2つのオブジェクトを比較します。

この方法であれば、IComparableインターフェイスを実装していない要素でも並び替えることができます。

Compareメソッドでは、1番目のパラメータと2番目のパラメータで渡されるオブジェクトを比較し、1番目の方が2番目より小さいときは負の整数、大きいときは正の整数、同じときは0を返すようにします。

以下に、文字列の長さで並び替えできるようにしたIComparerを実装したクラスの例を示します。

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 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
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))
        {
            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のような例外をスローしてもよいでしょう。

このクラスを使用して並び替えを行うコードを以下に示します。

VB.NET
コードを隠すコードを選択
'並び替えを行う配列を作成
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

'LengthComparerを使って、文字列の長さで並び替える
Dim comp As New LengthComparer()
Array.Sort(ary, comp)

'aryは { "b", "cc", "aaaaa" } となる
C#
コードを隠すコードを選択
//並び替えを行う配列を作成
string[] ary = new string[] { "b", "aaaaa", "cc" };

//LengthComparerを使って、文字列の長さで並び替える
LengthComparer comp = new LengthComparer();
Array.Sort(ary, comp);

//aryは { "b", "cc", "aaaaa" } となる

文字列の配列を大文字小文字を区別しないで並び替えたり、カルチャに依存しないで並べ替えたりするならば、あらかじめ用意されているIComparerを使うこともできます(ただし、.NET Framework 2.0以降)。詳しくは、「文字列の配列やコレクションを並び替える」で説明します。

IComparer<T>ジェネリックインターフェイスを実装する

.NET Framework 2.0以降では、IComparer<T>ジェネリックインターフェイスを実装した方が良いでしょう。

先ほどのLengthComparerクラスにIComparer<T>ジェネリックインターフェイスも実装した例を示します。使い方は、先ほどと同じです。

VB.NET
コードを隠すコードを選択
'並び替える方法を定義するクラス
'IComparerジェネリックインターフェイスを実装する
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))
    End Function
End Class
C#
コードを隠すコードを選択
//並び替える方法を定義するクラス
//IComparerジェネリックインターフェイスを実装する
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メソッドと同じです。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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;
}

このメソッドを使って並び替えを行うには、次のようにします。

VB.NET
コードを隠すコードを選択
'並び替えを行う配列を作成
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

'並び替えを行う
Array.Sort(ary, AddressOf CompareByLength)

'aryは { "b", "cc", "aaaaa" } となる
C#
コードを隠すコードを選択
//並び替えを行う配列を作成
string[] ary = new string[] { "b", "aaaaa", "cc" };

//並び替えを行う
Array.Sort(ary, CompareByLength);

//aryは { "b", "cc", "aaaaa" } となる

C#の匿名メソッドが使えるのであれば、上と同じことを次のように簡単に記述することができます。残念ながらVB.NETでは匿名メソッドを使えません。

C#
コードを隠すコードを選択
//並び替えを行う配列を作成
string[] ary = new string[] { "b", "aaaaa", "cc" };

//並び替えを行う
Array.Sort(ary,
    delegate(string x, string y) { return x.Length.CompareTo(y.Length); });

もしラムダ式が使えるのであれば(VB9、C#3.0、.NET Framework 3.5、Visual Studio 2008以降)、次のようにしてさらに簡単に記述することができます。(nullのチェックは省略しています。)

VB.NET
コードを隠すコードを選択
'並び替えを行う配列を作成
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

'並び替えを行う
Array.Sort(ary, _
    Function(x As String, y As String) x.Length.CompareTo(y.Length))
C#
コードを隠すコードを選択
//並び替えを行う配列を作成
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で普通に配列を並び替える例です。OrderByメソッドを使用しています。ここでは配列を並び替えていますが、コレクションでも同じようにできます。

VB.NET
コードを隠すコードを選択
'Imports System.Linq

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

'要素を昇順で並び替える
Dim query = strs.OrderBy(Function(s) s)

'クエリ式を使うと、次のように書ける
'Dim query = From s In strs _
'            Order By s _
'            Select s

'配列を作成する
Dim sortedStrs As String() = query.ToArray()

'sortedStrsは { "aaaaa", "b", "cc" } となる
C#
コードを隠すコードを選択
//using System.Linq;

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

//要素を昇順で並び替える
var query = strs.OrderBy(s => s);

//クエリ式を使うと、次のように書ける
//var query = from s in strs
//            orderby s
//            select s;

//配列を作成する
string[] sortedStrs = query.ToArray();

//sortedStrsは { "aaaaa", "b", "cc" } となる

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

VB.NET
コードを隠すコードを選択
'Imports System.Linq

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

'要素のLengthプロパティの大きさで並び替える
Dim query = strs.OrderBy(Function(s) s.Length)

'クエリ式を使うと、次のように書ける
'Dim query = From s In strs _
'            Order By s.Length _
'            Select s

'配列を作成する
Dim sortedStrs As String() = query.ToArray()

'sortedStrsは { "b", "cc", "aaaaa" } となる
C#
コードを隠すコードを選択
//using System.Linq;

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

//要素のLengthプロパティの大きさで並び替える
var query = strs.OrderBy(s => s.Length);

//クエリ式を使うと、次のように書ける
//var query = from s in strs
//            orderby s.Length
//            select s;

//配列を作成する
string[] sortedStrs = query.ToArray();

//sortedStrsは { "b", "cc", "aaaaa" } となる

文字列の長さで並び替え、同じ長さならば普通の文字列の大きさで並び替えるには、次のようにします。2番目以降の並び替えには、ThenByメソッドを使います。

VB.NET
コードを隠すコードを選択
'Imports System.Linq

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

'文字列の長さで並び替え、同じ長さならば普通の並び替え
Dim query = strs.OrderBy(Function(s) s.Length).ThenBy(Function(s) s)

'クエリ式を使うと、次のように書ける
'Dim query = From s In strs _
'            Order By s.Length, s _
'            Select s

'配列を作成する
Dim sortedStrs As String() = query.ToArray()

'sortedStrsは { "b", "d", "cc", "aaaaa" } となる
C#
コードを隠すコードを選択
//using System.Linq;

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

//文字列の長さで並び替え、同じ長さならば普通の並び替え
var query = strs.OrderBy(s => s.Length).ThenBy(s => s);

//クエリ式を使うと、次のように書ける
//var query = from s in strs
//            orderby s.Length, s
//            select s;

//配列を作成する
string[] sortedStrs = query.ToArray();

//sortedStrsは { "b", "d", "cc", "aaaaa" } となる
補足:上記の例でThenByをOrderByとした場合は、長さで並び替えた後、普通の並び替えが行われるため、結果としては、通常に並び替えを行い、同じならば長さで並び替えたのと同じになります。

文字列の配列を文字列の長さの降順で並び替えるには、次のようにOrderByDescendingメソッドを使います。なお2番目以降を降順で並び替える場合は、ThenByDescendingメソッドを使います。

VB.NET
コードを隠すコードを選択
'Imports System.Linq

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

'Lengthプロパティの大きさで降順に並び替える
Dim query = strs.OrderByDescending(Function(s) s.Length)

'クエリ式を使うと、次のように書ける
'Dim query = From s In strs _
'            Order By s.Length Descending _
'            Select s

'配列を作成する
Dim sortedStrs As String() = query.ToArray()

'sortedStrsは { "aaaaa", "cc", "b" } となる
C#
コードを隠すコードを選択
//using System.Linq;

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

//Lengthプロパティの大きさで降順に並び替える
var query = strs.OrderByDescending(s => s.Length);

//クエリ式を使うと、次のように書ける
//var query = from s in strs
//            orderby s.Length descending
//            select s;

//配列を作成する
string[] sortedStrs = query.ToArray();

//sortedStrsは { "aaaaa", "cc", "b" } となる

指定した方法で並び替える、特殊な方法

配列やコレクションを並び替える方法は、今まで紹介したものが一般的です。ここからは、指定した方法で並び替える方法として考えられる少し変わった方法をさらに幾つか紹介します。

SortedListクラスを使う

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

.NET Framework 2.0以降では、ジェネリックバージョンを使用できます。

キーは一意でなければならないため、複数の要素が同じ値のキーになるならば、この方法は使用できません。例えば下の例のように文字列の長さで並び替えたい時、同じ長さの文字列が複数存在していると、うまく行きません。

SortedListは、要素が追加された時に並び替えが行われるようです。要素を追加した時の操作はO(n)で、要素が末尾に追加されるときはO(log n)です。

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

VB.NET
コードを隠すコードを選択
'並び替えを行う配列
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

'SortedListを作成
Dim sl As New System.Collections.SortedList()
'文字列の長さをキーとしてコレクションに追加する
For Each s As String In ary
    sl(s.Length) = s
    'Addメソッドで追加すると、すでにキーが存在していると例外がスローされる
    'sl.Add(s.Length, s)
Next

'並び替えた結果を表示する
Console.WriteLine("SortedListで並び替え")
For i As Integer = 0 To sl.Count - 1
    Console.WriteLine("{0}: {1}", i, sl.GetByIndex(i))
Next
C#
コードを隠すコードを選択
//並び替えを行う配列
string[] ary = new string[] { "b", "aaaaa", "cc" };

//SortedListを作成
System.Collections.SortedList sl = new System.Collections.SortedList();
//文字列の長さをキーとしてコレクションに追加する
foreach (string s in ary)
{
    sl[s.Length] = s;
    //Addメソッドで追加すると、すでにキーが存在していると例外がスローされる
    //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)メソッドを使って文字列の長さで並び替えを行う例を示します。

VB.NET
コードを隠すコードを選択
'並び替えを行う配列
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

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

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

'aryは  { "b", "cc", "aaaaa" }
'keysは { 1, 2, 5 } になる
C#
コードを隠すコードを選択
//並び替えを行う配列
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);

//aryは  { "b", "cc", "aaaaa" }
//keysは { 1, 2, 5 } になる

どの方法が一番早いか

Array.Sortメソッド、List.Sortメソッド、LINQの3つの並び替え方法について、ベンチマークの結果が「Sort String Array」で紹介されています。これによると、List.Sortメソッドが若干速く、要素数が多いときはLINQが遅いようですが、3つの方法の速さの違いはそれほどないようです。ただしこのベンチマークでは、Sortメソッドは配列やコレクションのコピーを作成してから並び替えているため、コピーを作成しないで並び替えるのであれば、LINQとの差はもっと出るかもしれません。

文字列の並び替え

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

  • 履歴:
  • 2008/7/15 大幅に書き換える。検索の方法を別のページに移す。IComparableジェネリックインターフェイス、SortedListクラス、Array.Sort(Array, Array)メソッド、Comparisonジェネリックデリゲートの説明を追加。
  • 2008/8/27 LINQによる方法を追記。
  • 2009/8/18 幾つか修正。
  • 2009/8/19 匿名メソッドやラムダ式を使った方法の追加など。
  • 2011/2/7 構成の変更と全体的な書き直し。
  • 2013/8/18 説明やコードをできるだけ簡潔に書き直した。
  • 2016/2/12 Array.Sortのアルゴリズムが.NET Framework 4.5から変わったことを追記など。
  • 2016/6/3 「IComparer<T>ジェネリックインターフェイスを実装する」のVB.NETのサンプルに間違いがあったのを修正。

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

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • 「???を参照に追加します」の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。