Java List検索の決定版!indexOf・containsからラムダ式まで初心者向けに解説
生徒
「JavaのListの中にある特定のデータが入っているかどうかを調べたいのですが、どうすればいいですか?」
先生
「Javaでは、Listインターフェースに用意されているcontainsメソッドやindexOfメソッドを使うことで、簡単に中身を確認したり、場所を特定したりできますよ。」
生徒
「もし、もっと複雑な条件、例えば『特定の文字から始まる文字列』を探したい時はどうするんですか?」
先生
「その場合はJava 8から導入されたStream APIやラムダ式を使うのが便利ですね。基本から応用まで、順番に解説していきましょう!」
1. JavaのListを検索する主な3つの手法
プログラミングをしていると、ArrayListなどのListコレクションの中に、特定の要素が存在するかどうかを確認したり、その要素が何番目に格納されているかを取得したりしたい場面が頻繁にあります。JavaでListを検索する方法は、大きく分けて以下の3つのパターンが存在します。
- 存在確認: 指定した要素がリストの中に含まれているか(true/false)を調べる。
- 位置特定: 指定した要素がリストの何番目にあるか(インデックス番号)を調べる。
- 条件一致検索: 特定の値そのものではなく、「数値が100以上」や「特定の文字を含む」といった条件に合うものを探す。
初心者のうちは、まず最初の2つをマスターすることから始めましょう。後半では、モダンなJavaの書き方であるStream APIについても触れていきます。
2. containsメソッドで要素の有無を確認する
最もシンプルでよく使われるのが、containsメソッドです。このメソッドは、引数に指定したオブジェクトがListの中に存在するかどうかを判定し、存在すればtrue、存在しなければfalseを返します。単純に「あるかないか」だけを知りたい場合に非常に便利です。
例えば、果物の名前が入ったリストから「リンゴ」があるかを探すコードを書いてみましょう。大文字と小文字は区別される点に注意が必要です。
import java.util.ArrayList;
import java.util.List;
public class ListContainsExample {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Orange");
// "Banana"が含まれているか確認
if (fruits.contains("Banana")) {
System.out.println("バナナは見つかりました!");
} else {
System.out.println("バナナはありません。");
}
}
}
バナナは見つかりました!
3. indexOfメソッドで要素の位置を特定する
「そのデータがどこにあるのか」を知りたい場合は、indexOfメソッドを使用します。このメソッドは、指定した要素が最初に見つかった位置のインデックス番号を返します。Javaのインデックスは0から始まるため、1番目の要素は0、2番目の要素は1となります。もしリストの中に指定した要素が存在しない場合は、-1を返します。
これを利用して、要素の有無を判定することも可能ですが、基本的には位置を特定するために使われます。また、リストの中に同じ要素が複数ある場合、indexOfは最初に見つかった位置を返しますが、lastIndexOfを使うと最後に見つかった位置を返してくれます。
import java.util.ArrayList;
import java.util.List;
public class ListIndexOfExample {
public static void main(String[] args) {
List<String> colors = new ArrayList<>();
colors.add("Red");
colors.add("Blue");
colors.add("Green");
colors.add("Blue");
// "Blue"のインデックスを検索
int firstIndex = colors.indexOf("Blue");
int lastIndex = colors.lastIndexOf("Blue");
int unknownIndex = colors.indexOf("Yellow");
System.out.println("最初のBlueの場所: " + firstIndex);
System.out.println("最後のBlueの場所: " + lastIndex);
System.out.println("存在しないYellowの結果: " + unknownIndex);
}
}
最初のBlueの場所: 1
最後のBlue의場所: 3
存在しないYellowの結果: -1
4. ラムダ式とStream APIを使った高度な検索
Java 8以降、Stream APIを使うことで、より高度で柔軟な検索が可能になりました。「完全に一致するもの」だけでなく、「特定の条件を満たすもの」を抽出することができます。例えば、「名前に『a』が含まれる人を全員探す」や「年齢が20歳以上の最初の1人を取得する」といった処理です。
ここでは、anyMatch(条件に合うものが1つでもあるか)と、filter(条件に合うものを抽出する)を紹介します。これらは実務の現場でも非常によく使われるため、少しずつ慣れていきましょう。初心者の方には最初は難しく感じるかもしれませんが、パターンのように覚えてしまうのがコツです。
5. 条件に一致する最初の要素を見つける
特定の条件に一致する最初の要素を取得するには、stream().filter().findFirst()という組み合わせを使います。この際、結果はOptionalという型で返されます。これは、条件に合うものが見つからなかった場合に「空っぽ」であることを安全に扱うための仕組みです。
以下のサンプルでは、数値のリストから「50より大きい最初の数字」を検索しています。単なる値の比較だけでなく、オブジェクトのフィールド(メンバ変数)を対象にした検索も自由自在に行えるようになります。
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
public class ListStreamSearchExample {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(10, 25, 60, 45, 80);
// 50より大きい最初の数値を見つける
Optional<Integer> result = numbers.stream()
.filter(n -> n > 50)
.findFirst();
if (result.isPresent()) {
System.out.println("50より大きい最初の値: " + result.get());
} else {
System.out.println("条件に合う値はありません。");
}
}
}
50より大きい最初の値: 60
6. 自作クラスのオブジェクトをListから検索する場合
文字列や数値ではなく、自分で作成したクラス(例えば「Userクラス」や「Productクラス」)をListに格納している場合、containsやindexOfを使うには注意が必要です。デフォルトでは、これらのメソッドは「メモリ上の同一オブジェクトか(参照が同じか)」で判断します。内容が同じかどうかで判定したい場合は、そのクラスでequalsメソッドとhashCodeメソッドを正しくオーバーライド(再定義)しておく必要があります。
しかし、Stream APIを使えば、equalsをオーバーライドしていなくても、特定のフィールド(IDや名前など)をキーにして検索することができるため、非常に汎用性が高いと言えます。
7. 大量データ検索時のパフォーマンスと注意点
Listの検索において、要素数が数件から数百件程度であれば速度を気にする必要はほとんどありません。しかし、数万件、数百万件という膨大なデータを扱う場合、ArrayListの検索は先頭から順番に中身を確認していく「線形探索」という方式をとるため、時間がかかるようになります。
もし頻繁に特定のキーで検索を行うのであれば、ListではなくHashMapのような別のコレクションの使用を検討するべきです。用途に合わせて適切なデータ構造を選ぶことも、Javaエンジニアとしての重要なスキルの一歩です。まずはListでの検索をしっかり使い分けられるようになりましょう。
8. 検索結果が複数ある場合のフィルタリング
これまでは「1つ」を見つける方法が中心でしたが、条件に合うものを「すべて」取り出したいこともあります。その場合は、Stream APIのfilterメソッドを使い、最後にcollect(Collectors.toList())を使って新しいリストとして出力します。これにより、元のリストを汚さずに必要なデータだけを抽出した新しいリストを手に入れることができます。
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class ListFilterAllExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Tanaka", "Sato", "Suzuki", "Takahashi");
// "T"で始まる名前をすべて抽出
List<String> filteredNames = names.stream()
.filter(name -> name.startsWith("T"))
.collect(Collectors.toList());
System.out.println("Tで始まる名前リスト: " + filteredNames);
}
}
Tで始まる名前リスト: [Tanaka, Takahashi]