Java 逆引き集 | max/min/findFirst/findAny — 候補抽出

Java Java
スポンサーリンク

max / min / findFirst / findAny — 候補抽出

Stream API には「候補をひとつ取り出す」ための便利な終端操作がいくつかあります。
max/min は「最大値・最小値」、findFirst/findAny は「最初の要素・どれかひとつの要素」を返します。
初心者が混乱しやすい「Optional の扱い」や「並列ストリームでの違い」を、例題とテンプレートで整理します。


基本の役割

  • max(Comparator):
    → ストリームの最大要素を返す。
    → 戻り値は Optional<T>
  • min(Comparator):
    → ストリームの最小要素を返す。
    → 戻り値は Optional<T>
  • findFirst():
    → ストリームの最初の要素を返す。
    → 戻り値は Optional<T>
  • findAny():
    → ストリームの「どれかひとつ」を返す。並列ストリームでは最初でなくてもよい。
    → 戻り値は Optional<T>

基本コード例

最大値・最小値

import java.util.*;
import java.util.stream.*;

public class MaxMinExample {
    public static void main(String[] args) {
        List<Integer> nums = List.of(5, 2, 9, 1, 7);

        Optional<Integer> max = nums.stream().max(Integer::compare);
        Optional<Integer> min = nums.stream().min(Integer::compare);

        System.out.println("max=" + max.orElse(-1)); // 9
        System.out.println("min=" + min.orElse(-1)); // 1
    }
}
Java

最初の要素を取得

List<String> words = List.of("apple","banana","cherry");

Optional<String> first = words.stream().findFirst();
System.out.println(first.orElse("NA")); // apple
Java

どれかひとつを取得(並列ストリームで効く)

List<String> words = List.of("apple","banana","cherry");

Optional<String> any = words.parallelStream().findAny();
System.out.println(any.orElse("NA")); // "banana" など、順序保証なし
Java

例題で理解する

例題1: 学生の最高点・最低点

record Student(String name, int score) {}

List<Student> students = List.of(
    new Student("Tanaka",80),
    new Student("Sato",55),
    new Student("Ito",90)
);

Student top = students.stream()
    .max(Comparator.comparingInt(Student::score))
    .orElse(null);

Student bottom = students.stream()
    .min(Comparator.comparingInt(Student::score))
    .orElse(null);

System.out.println("最高点=" + top.name() + ", 最低点=" + bottom.name());
Java

例題2: ログの最初のエラーを見つける

List<String> logs = List.of("INFO start","WARN retry","ERROR failed","ERROR timeout");

String firstError = logs.stream()
    .filter(l -> l.startsWith("ERROR"))
    .findFirst()
    .orElse("No error");

System.out.println(firstError); // ERROR failed
Java

例題3: 並列処理で「どれかひとつ」を早く返す

List<Integer> nums = IntStream.rangeClosed(1, 1000).boxed().toList();

int anyEven = nums.parallelStream()
    .filter(n -> n % 2 == 0)
    .findAny()
    .orElse(-1);

System.out.println(anyEven); // 偶数のどれかひとつ(順序保証なし)
Java

テンプレート集

  • 最大値
Optional<T> max = stream.max(Comparator.comparing(x -> key));
Java
  • 最小値
Optional<T> min = stream.min(Comparator.comparing(x -> key));
Java
  • 最初の要素
Optional<T> first = stream.findFirst();
Java
  • どれかひとつ(並列で効率的)
Optional<T> any = stream.findAny();
Java
  • Optional の安全利用
T value = opt.orElse(defaultValue);
Java

落とし穴と回避策

  • 空ストリーム: max/min/findFirst/findAny は Optional.empty を返す。必ず orElseifPresent で安全に扱う。
  • 並列ストリームでの findAny: 順序保証がない。最初の要素が欲しいなら findFirst を使う。
  • Comparator の定義ミス: max/min は Comparator に依存。独自クラスでは正しく比較を定義する。
  • 大量データ: max/min は全件走査するため O(n)。必要なら事前に filter で絞り込む。

まとめ

  • max/min: 最大・最小を抽出。Comparator が必要。
  • findFirst: 最初の要素を返す(順序保証あり)。
  • findAny: どれかひとつを返す(並列で効率的)。
  • Optional: 空ストリームに備えて必ず安全に扱う。

👉 練習課題: 「社員リストから最高年齢の社員を max で取得」「ログから最初の WARN を findFirst で取得」してみると、候補抽出の便利さが体感できます。

タイトルとURLをコピーしました