コレクション【C#】

C#文法:コレクション:B10_Exists

前回までで、条件に合う要素そのものを取り出すArray.Findと、その位置を返すArray.FindIndexを見てきました。今回は、同じ検索系のメソッドであるArray.Existsを取り上げます。Existsは、条件に合う要素が配列の中に「存在するかどうか」だけをboolで返すメソッドです。存在するときはtrue、存在しないときはfalseになります。

図1 今回のテーマは Exists

図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 のシグネチャ

リスト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より大きい要素が存在する)

図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より大きい要素は存在しない)

図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 を呼び出す

図5 if 文の中で Find を呼び出す

ifの行にブレークポイントを置いて実行します。Existsがtrueを返すのでifの中に入り、Findで値を取りに行きます。result4を確認すると、1が返ってきています。

図6 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 にする

図7 ints の先頭を 0 にする

この状態でもう一度実行します。Existsはtrueを返してifの中に入り、Findの結果であるresult4は0になります。

図8 result4 は 0(存在を確認したうえでの 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#文法:コレクション

■非公開コース「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新機能」を観る