CodeGym /コース /C# SELF /C#でコレクション内の要素を探す方法

C#でコレクション内の要素を探す方法

C# SELF
レベル 29 , レッスン 1
使用可能

1. コレクションでの基本的な検索機能

よく“検索”“フィルタ”って同じ意味で使われるけど、プログラミングだとちょっと違うんだ。

  • フィルタ — コレクションの中から、ある条件に合う全ての要素を取り出したい時(例えば、10より大きい数字全部とか)。
  • 検索 — だいたいは1つの要素が欲しい時:最初に合うやつ、特定の値のやつ、またはコレクションにあるかどうかだけ知りたい時。

図書館で例えると:フィルタは“宇宙”テーマの本を全部集める感じ、検索は“ユリシーズって本あります?”とか“青い表紙の本で一番最初にあるのはどこ?”って聞く感じ。

.NETの主要なコレクション(配列、リスト、セット、ディクショナリなど)は、要素の検索や存在チェックのいろんな方法をサポートしてるよ。

よく使うメソッドを見てみよう:

コレクション 存在チェック インデックス検索 要素検索 キー検索
List<T>
Contains
IndexOf
Find
-
T[]
(配列)
Contains
/
Array.IndexOf
Array.IndexOf
- -
HashSet<T>
Contains
- - -
Dictionary<TKey,V>
ContainsKey
,
ContainsValue
- - インデクサ
[key]
がある

一部のメソッドは特定の型だけで使えるよ。

2. リストでの検索:List<T>

List<T>は、ランダムアクセスできる要素を保存するのに超便利なコレクション。主な検索メソッドはこれ:

要素の存在チェック:Contains

このメソッドは、リストに指定した要素が入ってるか教えてくれるよ:

List<string> fruits = new List<string> { "リンゴ", "バナナ", "キウイ" };
bool hasKiwi = fruits.Contains("キウイ"); // true
bool hasMango = fruits.Contains("マンゴー"); // false
Console.WriteLine(hasKiwi); // True

中身ではContainsはコレクションを順番に見て、Equalsメソッドで比較してるだけ。

インデックス検索:IndexOf

要素がどこにあるか(リスト内の番号)知りたい時はこれ:

int index = fruits.IndexOf("バナナ"); // 1(0から始まる)
int absentIndex = fruits.IndexOf("スイカ"); // -1(なかったら)
Console.WriteLine(index);
Console.WriteLine(absentIndex);

最初に合うやつを探す:Find

値じゃなくて条件で探したい時はFind(インデックスが欲しいならFindIndex)を使う:

// 名前が4文字より長い最初のフルーツを探す
string longFruit = fruits.Find(fruit => fruit.Length > 4); // "リンゴ"
Console.WriteLine(longFruit);

注意:何も見つからなかったら、型のデフォルト値(参照型ならnull)が返るよ。

複数の要素を探す:FindAll

フィルタ(全部欲しい時)はFindAllを使おう:

// 名前に「イ」が入ってるフルーツ全部
List<string> withI = fruits.FindAll(f => f.Contains('イ'));
foreach (var fruit in withI)
    Console.WriteLine(fruit); // "キウイ"

3. 配列での検索:Arrayクラスのメソッド

配列はFindメソッドがないけど、静的クラスArrayで色々できるよ:

int[] numbers = { 1, 2, 3, 2, 4 };
int pos = Array.IndexOf(numbers, 2); // 1 — 2の最初のインデックス
int lastPos = Array.LastIndexOf(numbers, 2); // 3 — 最後のインデックス
Console.WriteLine(pos + ", " + lastPos);

条件で探したい時は、普通にforループでOK:

int firstGreaterThanTwo = -1;
for (int i = 0; i < numbers.Length; i++)
{
    if (numbers[i] > 2)
    {
        firstGreaterThanTwo = numbers[i];
        break;
    }
}
Console.WriteLine(firstGreaterThanTwo); // 3

4. 全コレクション共通のメソッド

多くのコレクションはContainsIndexOfFindなどの検索メソッドを持ってる。もっと複雑なことしたい時は、自分でループ書こう。

例:頭文字が「バ」のフルーツがあるかチェック

bool hasB = false;
foreach (var f in fruits)
{
    if (f.StartsWith("バ"))
    {
        hasB = true;
        break;
    }
}
Console.WriteLine(hasB); // True

例:「イ」が入ってる最初のフルーツを探す

string withI = null;
foreach (var f in fruits)
{
    if (f.Contains('イ'))
    {
        withI = f;
        break;
    }
}
Console.WriteLine(withI); // "キウイ"

例:ユーザー定義オブジェクトでの検索

class Student
{
    public string Name;
    public int Group;
    public int Id;
}

List<Student> students = new List<Student>
{
    new Student { Name = "イワン", Group = 101, Id = 1 },
    new Student { Name = "マリア", Group = 101, Id = 2 },
    new Student { Name = "ピョートル", Group = 102, Id = 3 },
};

// Id == 2の学生を探す
Student maria = null;
foreach (var s in students)
{
    if (s.Id == 2)
    {
        maria = s;
        break;
    }
}
if (maria != null)
    Console.WriteLine(maria.Name); // "マリア"
else
    Console.WriteLine("学生が見つからなかった");

5. HashSet<T>での検索:「あるかないか」だけ!

セット(HashSet<T>)は「あるかないか」を超高速で調べるためのコレクション。インデックス検索はできないけど、存在チェックはめっちゃ速い:

HashSet<int> set = new HashSet<int> { 1, 3, 5, 7 };
bool hasThree = set.Contains(3); // True
Console.WriteLine(hasThree);

// 条件で探したい時(例えば偶数があるか):
bool hasEven = false;
foreach (var x in set)
{
    if (x % 2 == 0)
    {
        hasEven = true;
        break;
    }
}
Console.WriteLine(hasEven); // False

6. ディクショナリでの検索:Dictionary<TKey, TValue>

ディクショナリは「キーと値」のペアのコレクション。キーで探すのが超得意。

キーの存在チェック

Dictionary<int, string> idToName = new Dictionary<int, string>
{
    { 1, "ヴァーシャ" }, { 2, "カーチャ" }
};
if (idToName.ContainsKey(2))
    Console.WriteLine(idToName[2]); // "カーチャ"

キーで値を安全に探す

if (idToName.TryGetValue(3, out string result))
    Console.WriteLine(result);
else
    Console.WriteLine("そのIdの学生はいません"); // これが出る

値で探す(レア&遅い)

bool containsVasya = idToName.ContainsValue("ヴァーシャ");
Console.WriteLine(containsVasya); // True

値やキーの条件でエントリを探す

// 名前が「カ」で始まる最初のId
KeyValuePair<int, string> entry = default;
bool found = false;
foreach (var pair in idToName)
{
    if (pair.Value.StartsWith("カ"))
    {
        entry = pair;
        found = true;
        break;
    }
}
if (found)
    Console.WriteLine($"{entry.Key}: {entry.Value}"); // "2: カーチャ"

7. 便利なポイント

検索メソッドの表

コレクションの型 存在チェック Contains インデックス検索 IndexOf 条件検索 Find 安全なキー検索 TryGetValue
List<T>
はい はい はい(
Find
-
T[]
(配列)
はい(
Array.Contains
/ループ)
はい(
Array.IndexOf
ループ -
HashSet<T>
はい - いいえ(手動のみ) -
Dictionary<TKey,V>
はい(
ContainsKey
- 手動でキー/値で はい

イテレーティブな検索:自分で書いてみよう

もっと理解したいなら、自分で検索メソッドを書いてみよう。例えば、指定した値より大きい最初の要素のインデックスを探す:

static int FindFirstGreaterIndex(IEnumerable<int> collection, int minValue)
{
    int index = 0;
    foreach (var item in collection)
    {
        if (item > minValue)
            return index;
        index++;
    }
    return -1; // 見つからなかった
}

var nums = new List<int> { 1, 4, 7, 2 };
Console.WriteLine(FindFirstGreaterIndex(nums, 3)); // 1(数字4)

ここでは型(List<T>int[]HashSet<T>でもOK)や内部実装に縛られないよ。

実際のタスクでの検索の使い道

  • データベースでログイン名やメールでユーザーを探す。
  • カートにその商品が入ってるかチェック。
  • パラメータ名で設定を素早く探す。
  • 特定のステップがもう実行されたかチェック(workflowとか)。
  • ログ配列でエラーの位置を探す。

ミドルレベルの面接では必ず「コレクションで要素をどうやって探す?条件で最初/全部/インデックスを返すメソッドどう書く?」って聞かれるから、検索の練習は超大事!

8. 検索のよくあるミスや注意点

大事なポイント:検索メソッドはオブジェクトの比較方法に依存する。例えば、自作クラスをリストに入れると、デフォルトでは参照比較になる!「中身」で検索したいならEqualsGetHashCodeをオーバーライドしないとダメ(この辺は次のレクチャーで詳しくやるよ)。

問題例:

var s1 = new Student { Name = "エゴール", Id = 42 };
students.Add(s1);
// 同じIdと名前で新しいオブジェクトを作る:
var s2 = new Student { Name = "エゴール", Id = 42 };
Console.WriteLine(students.Contains(s2)); // False(!)

コンパイラはフィールドじゃなくて参照で比較してる(つまり別オブジェクト)。

コメント
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION