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

配列やコレクション内に指定された要素があるか調べ、その位置を知る

注意:指定した要素が存在するかを調べるだけの方法は「配列やコレクション内に指定された要素があるか調べる」に、フィルタ処理に関する説明は「配列やコレクションのフィルタ処理を行う(条件に合う要素を抜き出す)」にそれぞれ移動しました。

ここでは、配列やコレクション内に指定した値と一致する要素が含まれているかを調べ、その位置(インデックス)を知る方法を説明します。さらに、指定した条件にあった要素を検索する方法も紹介します。

なお、for文を使って配列内の要素を一つずつ調べる方法もありますが、説明するまでもないと思いますので、ここでは紹介しません。

IndexOfメソッドを使用した方法

IndexOfメソッドを使うことで、配列やコレクション内の要素を検索することができます。検索する値と一致する要素が見つかれば、はじめに見つかったインデックス番号を返します。見つからなければ、 -1 を返します。

補足:厳密にいえば、配列の場合は、要素が見つからなかった時に返される値は -1 と決まっている訳でなく、「配列の下限 - 1」となります。しかし、VB.NETやC#では配列の下限は 0 ですので、-1 と考えて大丈夫です。

IndexOfメソッドは、配列の場合は、スタティックメソッド(Array.IndexOfメソッド)です。コレクションでは、ArrayList(ArrayList.IndexOfメソッド)やList(List<T>.IndexOfメソッド)で使用でき、こちらはインスタンスメソッドです。

IndexOfメソッドは、各要素のEqualsメソッドを使って、検索する値と一致する要素を探しています。

補足:.NET Framework 1.1以前では、検索する値のEqualsメソッドを使って、要素との比較を行います。

IndexOfメソッドは順次検索を行うため、O(n)操作です。

以下にArray.IndexOfメソッドを使った例を示します。

VB.NET
コードを隠すコードを選択
'検索元の配列
Dim ary As String() = New String() {"red", "blue", "white", "blue", "red"}

'"blue"の位置を取得する
Dim index1 As Integer = Array.IndexOf(ary, "blue")
'"1"を返す

'"gold"の位置を取得する
Dim index2 As Integer = Array.IndexOf(ary, "gold")
'存在しないので、"-1"を返す
C#
コードを隠すコードを選択
//検索元の配列
string[] ary = new string[] { "red", "blue", "white", "blue", "red" };

//"blue"の位置を取得する
int index1 = Array.IndexOf(ary, "blue");
//"1"を返す

//"gold"の位置を取得する
int index2 = Array.IndexOf(ary, "gold");
//存在しないので、"-1"を返す

コレクション(List)の例は、以下のようになります。

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

'検索元のコレクション
Dim lst As New List(Of String)() From {"red", "blue", "white", "blue", "red"}

'"blue"の位置を取得する
Dim index3 As Integer = lst.IndexOf("blue")
'"1"を返す
C#
コードを隠すコードを選択
//using System.Collections.Generic;

//検索元のコレクション
List<string> lst = new List<string> { "red", "blue", "white", "blue", "red" };

//"blue"の位置を取得する
int index3 = lst.IndexOf("blue");
//"1"を返す
補足:.NET Framework 2.0以降では、IndexOfジェネリックメソッドが使用されます。

検索開始位置と範囲を指定する

IndexOfメソッドには、検索の開始位置と範囲を指定できます。以下の例では、配列内で一致する要素をすべて探しています。

VB.NET
コードを隠すコードを選択
'検索元の配列
Dim ary As String() = New String() {"red", "blue", "white", "blue", "red"}
'検索する値
Dim searchWord As String = "red"

'はじめの要素を探す
Dim foundIndex As Integer = Array.IndexOf(ary, searchWord)
While 0 <= foundIndex
    '要素が見つかったときは、その位置を表示する
    Console.WriteLine(foundIndex)

    If foundIndex + 1 < ary.Length Then
        '次の要素を検索する
        foundIndex = Array.IndexOf(ary, searchWord, foundIndex + 1)
    Else
        '最後まで検索したときはループを抜ける
        Exit While
    End If
End While
C#
コードを隠すコードを選択
//検索元の配列
string[] ary = new string[] { "red", "blue", "white", "blue", "red" };
//検索する値
string searchWord = "red";

//はじめの要素を探す
int foundIndex = Array.IndexOf(ary, searchWord);
while (0 <= foundIndex)
{
    //要素が見つかったときは、その位置を表示する
    Console.WriteLine(foundIndex);

    if (foundIndex + 1 < ary.Length)
    {
        //次の要素を検索する
        foundIndex = Array.IndexOf(ary, searchWord, foundIndex + 1);
    }
    else
    {
        //最後まで検索したときはループを抜ける
        break;
    }
}

最後から検索する

IndexOfメソッドの代わりにLastIndexOfメソッドを使うこともできます。LastIndexOfメソッドは、最後に見つかったインデックス番号を返します。

BinarySearchメソッドを使用した方法

BinarySearchメソッドを使って検索することもできます。BinarySearchメソッドで検索できる配列やコレクションは、Sortメソッドで昇順に並び替られている必要があります。よって、配列が並び替えられていない場合は、BinarySearchメソッドを呼び出す前にSortメソッドを呼び出しておく必要があります。

なおSortメソッドについて詳しくは、「配列やコレクション内の要素を並び替える」をご覧ください。

BinarySearchメソッドもIndexOfメソッドと同様、配列の場合は、スタティックメソッド(Array.BinarySearchメソッド)です。コレクションでは、ArrayList(ArrayList.BinarySearchメソッド)やList(List<T>.BinarySearchメソッド)で使用でき、こちらはインスタンスメソッドです。

BinarySearchメソッドでは、探している値と要素が一致するか調べるのにCompareToメソッドを使います。よって、要素にはIComparableインターフェイスが実装されている必要があります(ただし、後述するIComparerを指定する方法を使えば、IComparableインターフェイスが実装されていなくても大丈夫です)。

なおIComparableインターフェイスとCompareToメソッドについて詳しくは、「自作クラスの配列やコレクションでSortやBinarySearchができるようにする」をご覧ください。

検索する値が要素内に見つかった時は、そのインデックス番号を返します。見つからなかったときは、負の整数を返します。

補足:見つからなかったときに返す値にビットごとの補数演算子(VB.NETでは"Not"、C#では"~")を適用することにより、検索したオブジェクトより大きい最初の要素のインデックス番号を知ることができます。

BinarySearchメソッドは、O(log n)操作です。よって、並び替えられている配列に対しては、IndexOfよりも速いです。

Array.BinarySearchメソッドを使用した例は、以下のようになります。

VB.NET
コードを隠すコードを選択
'検索元の配列を作成
Dim ary As String() = New String() {"b", "aaaaa", "cc"}
'並び替える
Array.Sort(ary)

'"b"の位置を取得する
Dim index1 As Integer = Array.BinarySearch(ary, "b")
'"1"を返す

'"a"の位置を取得する
Dim index2 As Integer = Array.BinarySearch(ary, "a")
'存在しないので、"-1"を返す
C#
コードを隠すコードを選択
//検索元の配列を作成
string[] ary = new string[] { "b", "aaaaa", "cc" };
//並び替える
Array.Sort(ary);

//"b"の位置を取得する
int index1 = Array.BinarySearch(ary, "b");
//"1"を返す

//"a"の位置を取得する
int index2 = Array.BinarySearch(ary, "a");
//存在しないので、"-1"を返す

コレクション(List)の例は、以下のようになります。

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

'検索元のコレクションを作成
Dim lst As New List(Of String)() From {"b", "aaaaa", "cc"}
'並び替える
lst.Sort()

'"b"の位置を取得する
Dim index3 As Integer = lst.BinarySearch("b")
'"1"を返す
C#
コードを隠すコードを選択
//using System.Collections.Generic;

//検索元のコレクションを作成
List<string> lst = new List<string> { "b", "aaaaa", "cc" };
//並び替える
lst.Sort();

//"b"の位置を取得する
int index3 = lst.BinarySearch("b");
//"1"を返す
補足:.NET Framework 2.0以降では、BinarySearchジェネリックメソッドが使用されます。

IComparerを指定して、一致する条件を変更する

上記の方法では要素を検索する方法が決められており、それ以外の条件で検索することができません。しかしIComparerを使用することで、検索する要素の条件を変更することができます。例えば、値がある数以下の要素を探したり、オブジェクトのプロパティの値が同じ要素を探したりしたいときに役に立ちます。

IComparerを使用するには、自分でIComparerを実装したクラスを作成し、探したい要素の条件と一致する時にCompareメソッドが0を返すようにします。

IComparerについては、「配列やコレクション内の要素を並び替える」で詳しく説明していますので、そちらをご覧ください。

文字列の配列やコレクションを並び替える」で紹介した、ひらがなとカタカナ、全角と半角を区別しないで文字列を比較できるCultureInfoCompareクラスを使って、文字列を検索する例を示します。

VB.NET
コードを隠すコードを選択
'検索元の配列を作成
Dim ary As String() = New String() {"あ", "い", "う"}

'日本語カルチャのCultureInfoを作成
Dim ci As New System.Globalization.CultureInfo("ja-JP")
'CultureInfoCompareを作成する。ひらがなとカタカナ、半角と全角を区別しない
Dim cmp As New CultureInfoCompare(ci, _
    System.Globalization.CompareOptions.IgnoreKanaType Or _
    System.Globalization.CompareOptions.IgnoreWidth)

'並び替える
Array.Sort(ary, cmp)

'検索する
Dim foundIndex As Integer = Array.BinarySearch(ary, "ウ", cmp)

'結果を表示
Console.WriteLine(foundIndex)
'2
C#
コードを隠すコードを選択
//検索元の配列を作成
string[] ary = new string[] { "あ", "い", "う" };

//日本語カルチャのCultureInfoを作成
System.Globalization.CultureInfo ci =
    new System.Globalization.CultureInfo("ja-JP");
//CultureInfoCompareを作成する。ひらがなとカタカナ、半角と全角を区別しない
CultureInfoCompare cmp = new CultureInfoCompare(ci,
    System.Globalization.CompareOptions.IgnoreKanaType |
    System.Globalization.CompareOptions.IgnoreWidth);

//並び替える
Array.Sort(ary, cmp);

//検索する
int foundIndex = Array.BinarySearch(ary, "ウ", cmp);

//結果を表示
Console.WriteLine(foundIndex);
//2

文字列を検索する時の注意点

文字列の配列やコレクションを大文字小文字を区別しないで比較したり、カルチャに依存しないで比較したりするならば、あらかじめ用意されているIComparerを実装したクラスを使うこともできます。このようなクラスのインスタンスは、StringComparerクラスのプロパティから取得できます。ただし、StringComparerクラスは.NET Framework 2.0以降にしか存在しません。StringComparerクラスについては、「文字列の配列やコレクションを並び替える」で詳しく説明しています。

1つ例を示します。ここではStringComparer.InvariantCultureIgnoreCaseを使って、インバリアントカルチャで大文字と小文字を区別せずに文字列を検索しています。

VB.NET
コードを隠すコードを選択
'検索元の配列を作成
Dim ary As String() = New String() {"a", "b", "c"}

'並び替える
Dim comp As StringComparer = StringComparer.InvariantCultureIgnoreCase
Array.Sort(ary, comp)

'BinarySearchで"A"と同じ要素を大文字小文字を無視して検索する
'"0"を返す
Dim i As Integer = Array.BinarySearch(ary, "A", comp)

Console.WriteLine(ary(i))
'"a"と表示される
C#
コードを隠すコードを選択
//検索元の配列を作成
string[] ary = new string[] { "a", "b", "c" };

//並び替える
StringComparer comp = StringComparer.InvariantCultureIgnoreCase;
Array.Sort(ary, comp);

//BinarySearchで"A"と同じ要素を大文字小文字を無視して検索する
//"0"を返す
int i = Array.BinarySearch(ary, "A", comp);

Console.WriteLine(ary[i]);
//"a"と表示される

BinarySearchメソッドを使って文字列を検索する場合、Sortを呼び出した時と同じカルチャを使ってBinarySearchも呼び出しているかという点に注意してください。StringComparerを指定せずにSortメソッドを呼び出した時、並び替え方には現在のカルチャが使用されます。BinarySearchメソッドも同じです。よって、Sortを呼び出した後で現在のカルチャを変更し、その後BinarySearchを呼び出したとしたら、SortとBinarySearchで並び替え方が変わってしまいます。そうすると、正しく検索されないかもしれません。

もしSortを呼び出してからBinarySearchが呼び出されるまでの間に現在のカルチャが変更されてしまう可能性があるならば(特に、Sortしたデータを永続化した時など)、現在のカルチャを使用しないで並び替えと検索を行うようにしてください。そのためには、上記の例のようにインバリアントカルチャを使用したり、序数の並び替えを使用すると良いでしょう。

FindIndexメソッドを使用した方法

FindIndexメソッドを使うと、指定した条件と一致する要素を探して、最初に見つかったインデックスを取得することができます。見つからなかったときは、-1を返します。このメソッドは、.NET Framework 2.0以降で使用できます。

検索する条件はPredicateジェネリックデリゲートと同じシグネチャのメソッドで指定し、検索条件に合致したときにTrueを返すようにします。

このメソッドも配列ではスタティックメソッド(Array.FindIndexメソッド)で、Listではインスタンスメソッド(List<T>.FindIndexメソッド)です。

FindIndexメソッドも順次検索のため、O(n)操作です。

以下の例では、長さが1の文字列を検索しています。

VB.NET
コードを隠すコードを選択
'文字列の長さが1か調べるメソッド
Private Shared Function IsLengthOne(str As String) As Boolean
    Return str.Length = 1
End Function

'エントリポイント
Public Shared Sub Main()
'検索元の配列を作成
    Dim ary As String() = New String() {"b", "aaaaa", "cc"}

    '文字列の長さが1の要素を検索し、インデックスを取得
    Dim i As Integer = Array.FindIndex(ary, AddressOf IsLengthOne)
    'i は 0 になる

    Console.ReadLine()
End Sub
C#
コードを隠すコードを選択
//文字列の長さが1か調べるメソッド
private static bool IsLengthOne(string str)
{
    return str.Length == 1;
}

//エントリポイント
public static void Main()
{
    //検索元の配列を作成
    string[] ary = new string[] { "b", "aaaaa", "cc" };

    //文字列の長さが1の要素を検索し、インデックスを取得
    int i = Array.FindIndex(ary, IsLengthOne);
    //i は 0 になる

    Console.ReadLine();
}

C#では、匿名メソッドを使うともっと簡単に記述できます。残念ながら、匿名メソッドはVB.NETでは使えません。

C#
コードを隠すコードを選択
//検索元の配列を作成
string[] ary = new string[] { "b", "aaaaa", "cc" };

//文字列の長さが1の要素を検索し、インデックスを取得
int i = Array.FindIndex(ary, delegate(string s) { return s.Length == 1; });
//i は 0 になる

ラムダ式が使えるのであれば(VB9、C#3.0、.NET Framework 3.5、Visual Studio 2008以降)、次のようにさらに簡単に記述することができます。

VB.NET
コードを隠すコードを選択
'検索元の配列を作成
Dim ary As String() = New String() {"b", "aaaaa", "cc"}

'文字列の長さが1の要素を検索し、インデックスを取得
Dim i As Integer = Array.FindIndex(ary, Function(s) s.Length = 1)
'i は 0 になる
C#
コードを隠すコードを選択
//検索元の配列を作成
string[] ary = new string[] { "b", "aaaaa", "cc" };

//文字列の長さが1の要素を検索し、インデックスを取得
int i = Array.FindIndex(ary, s => s.Length == 1);
//i は 0 になる

FindIndexメソッドと同様のメソッドに、FindLastIndexメソッドがあります。FindLastIndexメソッドは、最後に見つかった要素のインデックスを返します。

また、FindIndexメソッドに似たメソッドに、FindメソッドFindLastメソッドがあります。Findメソッドは条件に合った最初の要素を返し、FindLastメソッドは最後の要素を返します。見つからなければ、型Tの既定値を返します。

指定された要素が存在するかだけを調べる

以前このページで紹介していた「Containsメソッドを使用する方法」、「IEqualityComparerを指定して、一致する条件を変更する」、「StringComparerクラスを使って、大文字と小文字を区別しないで検索する」、「Existsジェネリックメソッドを使用する方法」、「Anyメソッドを使用する方法」は、「配列やコレクション内に指定された要素があるか調べる」に移動しました。

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

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