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 を返す。必ず
orElseやifPresentで安全に扱う。 - 並列ストリームでの findAny: 順序保証がない。最初の要素が欲しいなら findFirst を使う。
- Comparator の定義ミス: max/min は Comparator に依存。独自クラスでは正しく比較を定義する。
- 大量データ: max/min は全件走査するため O(n)。必要なら事前に filter で絞り込む。
まとめ
- max/min: 最大・最小を抽出。Comparator が必要。
- findFirst: 最初の要素を返す(順序保証あり)。
- findAny: どれかひとつを返す(並列で効率的)。
- Optional: 空ストリームに備えて必ず安全に扱う。
👉 練習課題: 「社員リストから最高年齢の社員を max で取得」「ログから最初の WARN を findFirst で取得」してみると、候補抽出の便利さが体感できます。
