前回は、条件に合う要素の「位置」を取得するArray.FindIndexを取り上げました。今回はその仲間であるArray.Findを見ていきます。使い方はFindIndexとほとんど同じですが、返ってくるものがインデックスではなく、値そのもの(要素そのもの)である点が異なります。
あわせて、条件に合う要素が見つからなかったときに何が返るのか、そして数値型の配列で使う場合の注意点も確認します。
ボタンの作成とデータの準備
これまでと同じように、フォームに新しいボタンを1つ配置し、Textを「Find」にします。配置したボタンをダブルクリックして、クリックイベントのハンドラー(button8_Click)を生成します。
図1 フォームに「Find」ボタンを追加する
検索対象のデータは、前回のFindIndexのレクチャーで作成した文字列の配列をそのまま使います。button7_Clickの中にあるdataの定義をコピーして、今回のbutton8_Clickに貼り付けます。
図2 前回(button7_Click)の文字列データをコピーする
リスト1 検索対象のデータを用意する
private void button8_Click(object sender, EventArgs e)
{
//ラムダ式(条件)で検索
var data = new[]
{ "AAA", "BBBB", "CCC", "D", "EEEE", "ABCDE" };
//Findで値そのもの(要素)が1件とれる
}
図3 貼り付けたデータと、今回のテーマを示すコメント
Array.Findで要素そのものを取得する
Array.Findの書き方はFindIndexと同じです。第1引数に検索対象の配列、第2引数に条件を指定します。Array.Find(まで入力すると、インテリセンスにArray.Find<T>(T[] array, Predicate<T> match)というシグネチャが表示されます。
図4 Array.Find<T>(T[] array, Predicate<T> match) のシグネチャ
第2引数の型はPredicate<T>です。これは「T型の値を1つ受け取ってboolを返す」処理を表すデリゲートなので、前回と同様にラムダ式で条件を書けます。ここでは「文字数が4文字のもの」という条件を指定します。
リスト2 4文字の要素をFindで検索する
//Findで値そのもの(要素)が1件とれる var result1 = Array.Find(data, x => x.Length == 4);
ブレークポイントを置いて実行し、result1にマウスカーソルを合わせて中身を確認します。
図5 result1 には “BBBB” が入っている
結果は"BBBB"です。配列の中には4文字の要素として"BBBB"と"EEEE"の2つがありますが、Findが返すのは最初に見つかった1件だけです。前回のFindIndexでは同じ条件でインデックスの1が返ってきましたが、Findではその位置にある要素そのものが返ってくる、という関係になっています。
リスト3 FindIndexとFindの戻り値の違い
var data = new[]
{ "AAA", "BBBB", "CCC", "D", "EEEE", "ABCDE" };
var index = Array.FindIndex(data, x => x.Length == 4); //1(位置)
var value = Array.Find(data, x => x.Length == 4); //"BBBB"(要素そのもの)
見つからない場合はどうなるか
次に、条件に合う要素が1つもない場合を試します。99文字の文字列は配列の中に存在しないので、次のコードはヒットしないはずです。
リスト4 ヒットしない条件で検索する
var result1 = Array.Find(data, x => x.Length == 4); var result1b = Array.Find(data, x => x.Length == 99);
図6 ヒットしない場合、result1b は null になる
result1bはnullになりました。ただし、「見つからなければ必ずnullになる」というわけではありません。Findは、見つからなかったときにその型の既定値、つまりdefault(T)を返します。既定値は型ごとに決まっています。
図7 型ごとの既定値(数値=0、bool=false、class=null)
| 型の種類 | 例 | 見つからないときの戻り値 |
|---|---|---|
| 数値型 | int、double など |
0 |
| bool | bool |
false |
| 参照型(クラス) | string、自作クラス など |
null |
stringは参照型なので、今回のresult1bはnullになっていた、ということです。
int配列で試す
それでは、数値型の場合を実際に確認してみます。intの配列を用意し、「2より大きいもの」という条件でFindします。
リスト5 int配列をFindで検索する
var ints = new[] { 1, 2, 3, 80 };
var result2 = Array.Find(ints, x => x > 2);
図8 result2 には 3 が入っている
2より大きい要素は3と80の2つですが、最初に見つかるのは3なので、result2は3になります。ここまでは文字列のときと同じ動きです。
続いて、ヒットしない条件として「99より大きいもの」を指定します。
リスト6 int配列でヒットしない条件を指定する
var result2b = Array.Find(ints, x => x > 99);
図9 ヒットしない場合、result2b は 0 になる
result2bは0になりました。intの既定値が0だからです。このように、数値型では見つからなかったときに0系の値が返ってきます。
数値型では「0」に注意する
ここが今回いちばん注意したいところです。数値の配列には、データとしての0が入っているかもしれません。たとえば配列を次のように変更したとします。
リスト7 データとして0を含む配列
var ints = new[] { 1, 2, 3, 0, 80 };
var result2 = Array.Find(ints, x => x > 2);
//数値型などで存在しない場合ゼロになるので注意
var result2b = Array.Find(ints, x => x > 99);
図10 配列に 0 を追加し、注意点をコメントに残す
このときFindの結果が0だったとしても、それが「配列の中にあったデータの0」なのか、「見つからなかったために返ってきた既定値の0」なのか、戻り値だけでは区別がつきません。次のコードを見ると、その紛らわしさがよくわかります。
リスト8 戻り値の0だけでは区別できない例
var ints = new[] { 1, 2, 3, 0, 80 };
var a = Array.Find(ints, x => x < 1); //0(データとして存在する0)
var b = Array.Find(ints, x => x > 99); //0(見つからなかったときの既定値)
対策:Index系のメソッドやExistsでチェックする
対策の1つ目は、前回扱ったIndex系のメソッドで先に確認することです。FindIndexであれば、見つからない場合は-1が返るので、存在しないことをはっきり判定できます。
リスト9 FindIndexなら見つからない場合に-1が返る
var result2b = Array.Find(ints, x => x > 99); //0 var result2c = Array.FindIndex(ints, x => x > 99); //-1
図11 同じ条件でも FindIndex は -1 を返す
IndexOfやFindIndexのように、存在しなければ-1が返るメソッドで判定し、見つかった場合はそのインデックスで要素を取り出す、という書き方ができます。
リスト10 FindIndexで存在を確認してから要素を取り出す
var index = Array.FindIndex(ints, x => x > 99);
if (index >= 0)
{
//見つかった場合だけ要素を取り出す
var value = ints[index];
}
else
{
//見つからなかった場合の処理
}
対策の2つ目は、次回取り上げるArray.Existsを併用することです。Existsは条件に合う要素が存在するかどうかをtrue/falseで返します。先にExistsで確認し、存在していたらFindする、という形にすれば、データの0と見つからなかったときの0を区別できます。ExistsがtrueなのにFindの結果が0だった場合は、それは本当にデータとしての0だと判断できるわけです。
リスト11 Existsで存在を確認してからFindする(Existsの詳細は次回)
if (Array.Exists(ints, x => x > 99))
{
//存在する場合だけFindする
//ここで0が返ってきたら、それはデータとしての0
var value = Array.Find(ints, x => x > 99);
}
else
{
//存在しない(Findを呼ぶと既定値の0が返る状態)
}
図12 対策をコメントとして残す
今回のコード全体
リスト12 button8_Click(完成形)
private void button8_Click(object sender, EventArgs e)
{
//ラムダ式(条件)で検索
var data = new[]
{ "AAA", "BBBB", "CCC", "D", "EEEE", "ABCDE" };
//Findで値そのもの(要素)が1件とれる
var result1 = Array.Find(data, x => x.Length == 4); //"BBBB"
var result1b = Array.Find(data, x => x.Length == 99); //null
var ints = new[] { 1, 2, 3, 0, 80 };
var result2 = Array.Find(ints, x => x > 2); //3
//数値型などで存在しない場合ゼロになるので注意
var result2b = Array.Find(ints, x => x > 99); //0
var result2c = Array.FindIndex(ints, x => x > 99); //-1
//index系でチェックするか、Existsなどを使う
}
まとめ
Array.Findは、条件に合う最初の要素そのものを返します。書き方はFindIndexと同じで、第2引数にラムダ式で条件を指定します。
条件に合う要素が見つからない場合は、その型の既定値(default(T))が返ります。stringなどの参照型ならnull、数値型なら0、boolならfalseです。
とくに数値型では、データとしての0と、見つからなかったときの0が区別できなくなります。IndexOfやFindIndexのように-1で判定できるメソッドを使うか、Existsで存在を確認してからFindするようにしてください。次回は、そのExistsを取り上げます。
■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る
A01_はじめに
A02_プロジェクトの作成
B01_配列とは
B02_配列の生成とアクセス
B03_生成と同時に値を設定する
B04_型推論による生成
B05_メソッドの引数などにする場合の注意点
B06_Length
B07_IndexOfでの検索
B08_FindIndexでの検索
B09_Find
B10_Exists
B11_FindAllとFindLast
B12_誤ったコピー
B13_Array.Copy
B14_範囲指定のコピー
B15_Resize
C01_ArrayList
C02_List
C03_List 動的な要素の変更
C04_Listのコンストラクタ
C05_Listのコンストラクタ_Capacity
C06_ListTからArrayクラスのメソッドが呼ばれている
D01_コレクションインタフェースとは
D02_異なるコレクションクラスに互換性を持たせる
D03_インタフェースの階層構造
D04_クライアントができる操作を制限させる
D05_クライアントができる操作を制限させる_後半
D06_Enumerableの拡張メソッドに関して
D07_ReadOnly系のコレクションインタフェース
D08_AsReadOnly
D09_ToListでコピーする
D10_ListTはprivateで使う
■非公開コース「C#14新機能」プレゼント:
非公開コース「C#14新機能」(80分)をご覧になりたい方は
こちらからURLとパスワードを発行していますので、ご覧になってみてください。
非公開コース「C#14新機能」を観る



![図4 Array.Find<T>(T[] array, Predicate<T> match) のシグネチャ](https://anderson02.com/wp-content/uploads/2026/10/collection-b09-fig04.jpg)







