前回までで、条件に合う要素そのものを取り出すArray.Findと、その位置を返すArray.FindIndexを見てきました。今回は、同じ検索系のメソッドであるArray.Existsを取り上げます。Existsは、条件に合う要素が配列の中に「存在するかどうか」だけをboolで返すメソッドです。存在するときはtrue、存在しないときはfalseになります。
図1 今回のテーマは Exists
前回までのコードの確認
今回も、前回のFindのレクチャーで使ったintの配列intsを引き続き使います。前回の時点でのコードは次のようになっていました。
リスト1 前回までのコード(Find/FindIndex)
var ints = new[] { 1, 2, 3, 0, 80 };
var result2 = Array.Find(ints, x => x > 2);
//数値型などで存在しない場合ゼロになるので注意
var result2b = Array.Find(ints, x => x > 99);
var result2c = Array.FindIndex(ints, x => x > 99);
//index系でチェックするか、Existsなどを使う
ポイントは最後のコメントです。Array.Findは、条件に合う要素が見つからなかった場合に、その型の既定値を返します。intであれば0です。そのためresult2bは0になりますが、これだけでは「0というデータが見つかった」のか「見つからなかったので0が返ってきた」のかを区別できません。この問題への対処として、FindIndexのようなインデックス系のメソッドでチェックする方法と、今回のExistsを使う方法があります。
Array.Existsの書き方
それではExistsを書いていきます。Array.と入力してExistsを選択すると、次のようなシグネチャが表示されます。
図2 Array.Exists のシグネチャ
リスト2 Array.Existsの定義
public static bool Exists<T>(T[] array, Predicate<T> match);
引数の形はArray.Findとまったく同じです。第1引数に検索対象の配列、第2引数に条件を表すPredicate<T>(Tを受け取ってboolを返すデリゲート)を指定します。違いは戻り値だけで、Findが要素そのもの(T)を返すのに対して、Existsはboolを返します。
第1引数にintsを指定し、条件は例として「2より大きいもの」にしてみます。結果はresult3という変数で受け取ります。
リスト3 2より大きい要素が存在するか
var result3 = Array.Exists(ints, x => x > 2);
ブレークポイントを置いて実行し、result3にマウスカーソルを合わせると、trueになっていることが確認できます。intsの中には3や80といった、2より大きい値が入っているためです。
図3 result3 は true(2より大きい要素が存在する)
存在しない場合はfalseになる
次に、条件に合う要素が存在しないケースを確認します。intsの中身は80までしかないので、条件を「80より大きいもの」に変えた行を追加します。
リスト4 80より大きい要素が存在するか
var result3 = Array.Exists(ints, x => x > 2); var result3b = Array.Exists(ints, x => x > 80);
実行してresult3bを確認すると、今度はfalseになっています。80「より大きい」という条件なので、80自身は条件に合いません。該当する要素が1つもないためfalseが返ってきた、ということです。
図4 result3b は false(80より大きい要素は存在しない)
ExistsとFindを組み合わせる
ここで、前回お伝えした問題に戻ります。Findの結果が0だったとき、それが「データとしての0」なのか「存在しないときの0」なのかを見極めるために、Existsが使えます。先にExistsで存在を確認し、trueの場合にだけFindで値を取りに行く、という書き方です。
例として、条件を「-10より大きいもの」にしてif文を書きます。
リスト5 Existsで存在を確認してからFindで取得する
if (Array.Exists(ints, x => x > -10))
{
var result4 = Array.Find(ints, x => x > -10);
}
図5 if 文の中で Find を呼び出す
ifの行にブレークポイントを置いて実行します。Existsがtrueを返すのでifの中に入り、Findで値を取りに行きます。result4を確認すると、1が返ってきています。
図6 result4 は 1(先頭から順にチェックされる)
intsの中には0も入っていて、0も「-10より大きい」という条件には合っています。それでも1が返ってくるのは、Findが配列の先頭から順番にチェックして、最初に条件に合った要素を返すからです。現在の並びは{ 1, 2, 3, 0, 80 }なので、先頭の1が最初に条件に合います。
データとしての0を取得する
そこで、0が最初に見つかるように、配列の並びを変えてみます。0を先頭に移動します。
リスト6 0を先頭に移動する
var ints = new[] { 0, 1, 2, 3, 80 };
図7 ints の先頭を 0 にする
この状態でもう一度実行します。Existsはtrueを返してifの中に入り、Findの結果であるresult4は0になります。
図8 result4 は 0(存在を確認したうえでの 0)
今回の0は、Existsで「データがある」と確認したうえで取りに行った結果です。つまり「見つからなかったので既定値の0が返ってきた」のではなく、「0というデータが見つかった」のだと分かります。Findだけでは区別できなかった2つの0を、Existsと組み合わせることで見極められるようになりました。
まとめ
Array.Existsは、条件に合う要素が存在すればtrue、存在しなければfalseを返します。値型の配列でFindを使うときは、Existsで存在を確認してから取得する形で使ってみてください。今回のレクチャー終了時点でのコードは次のとおりです。
リスト7 今回の完成コード
var ints = new[] { 0, 1, 2, 3, 80 };
var result2 = Array.Find(ints, x => x > 2);
//数値型などで存在しない場合ゼロになるので注意
var result2b = Array.Find(ints, x => x > 99);
var result2c = Array.FindIndex(ints, x => x > 99);
//index系でチェックするか、Existsなどを使う
var result3 = Array.Exists(ints, x => x > 2); // true
var result3b = Array.Exists(ints, x => x > 80); // false
if (Array.Exists(ints, x => x > -10))
{
var result4 = Array.Find(ints, x => x > -10); // 0
}
■非公開コース「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新機能」を観る







