「最大値取得」は“いちばん大きいものを安全に一発で取り出す”ユーティリティ
業務コードでは、「最大値」が欲しい場面がよく出てきます。
最大金額、最新日付、最大スコア、最大バージョン番号…。
毎回 for 文で
int max = Integer.MIN_VALUE;
for (int v : values) {
if (v > max) {
max = v;
}
}
Javaと書いても動きますが、
null や空コレクション、オブジェクトの比較などが絡むと、
一気にバグりやすく、読みにくくなります。
そこで「最大値取得」をユーティリティとして切り出しておくと、
“最大値をどう扱うか”のルールを一箇所に閉じ込められます。
基本形:数値Listから最大値を取得する
Stream を使った最小限の書き方
まずは、List<Integer> から最大値を取りたいケースです。
import java.util.List;
public class MaxBasic {
public static void main(String[] args) {
List<Integer> scores = List.of(70, 85, 90);
int max = scores.stream()
.mapToInt(Integer::intValue)
.max()
.orElse(0); // 空なら 0 とする
System.out.println(max); // 90
}
}
Javaここで押さえてほしいポイントは二つです。
一つ目は、mapToInt で「int専用のストリーム」に変換してから max() を呼んでいることです。max() は「最大値を計算する」メソッドですが、
空のストリームに対しては「値がない」ので OptionalInt を返します。
二つ目は、orElse(0) で「空だったときのデフォルト値」を決めていることです。
ここをどうするか(0にするのか、例外にするのか、nullにするのか)は、
プロジェクト全体でルールを決めておくとよいです。
最大値取得をユーティリティメソッドにまとめる
「null や空Listをどう扱うか」を一箇所に閉じ込める
同じような最大値取得を何度も書くなら、
ユーティリティにしてしまうとスッキリします。
import java.util.List;
public final class Maxes {
private Maxes() {}
public static int maxIntOrDefault(List<Integer> source, int defaultValue) {
if (source == null || source.isEmpty()) {
return defaultValue;
}
return source.stream()
.mapToInt(Integer::intValue)
.max()
.orElse(defaultValue);
}
}
Java使い方はこうです。
List<Integer> scores = List.of(70, 85, 90);
int max = Maxes.maxIntOrDefault(scores, 0); // 空なら 0
Javaここでの重要ポイントは、「null と空Listの扱いをユーティリティ側で決めている」ことです。
呼び出し側は「最大値が欲しい」「空なら 0 でいい」とだけ考えればよく、null チェックや Optional の扱いを毎回書かなくて済みます。
オブジェクト一覧から「特定の項目の最大値」を取得する
例:商品一覧から「最大価格」を取り出す
業務では、単なる List<Integer> よりも、
「オブジェクトの特定のフィールドの最大値」が欲しいことが多いです。
class Item {
private final String name;
private final int price;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public String getName() { return name; }
public int getPrice() { return price; }
}
Javaこれを集計するユーティリティは、こう書けます。
import java.util.List;
import java.util.function.ToIntFunction;
public final class Maxes {
private Maxes() {}
public static <T> int maxIntOrDefault(
List<T> source,
ToIntFunction<? super T> mapper,
int defaultValue
) {
if (source == null || source.isEmpty()) {
return defaultValue;
}
return source.stream()
.mapToInt(mapper)
.max()
.orElse(defaultValue);
}
}
Java使い方はこうです。
List<Item> items = List.of(
new Item("A", 100),
new Item("B", 250),
new Item("C", 180)
);
int maxPrice = Maxes.maxIntOrDefault(items, Item::getPrice, 0);
System.out.println(maxPrice); // 250
Javaここで深掘りしたい重要ポイントは三つです。
一つ目は、「ToIntFunction<? super T> mapper が“どの項目を最大化するか”を表している」ことです。Item::getPrice を渡すことで、「価格の最大値が欲しい」という意図が明確になります。
二つ目は、「ユーティリティ側は“どう最大値を計算するか”だけを知っていて、“何を最大化するか”は呼び出し側が決める」構造になっていることです。
これにより、同じメソッドで「価格」「数量」「スコア」など、何でも最大値を取れます。
三つ目は、「空や null の扱いを一箇所に閉じ込めている」ことです。
プロジェクト全体で「空なら 0」「空なら -1」など、ルールを統一できます。
「最大の要素そのもの」を取得する(Comparator 利用)
例:最も高い価格の商品オブジェクトを取得する
「最大値」ではなく、「最大値を持つオブジェクトそのもの」が欲しいことも多いです。
例えば、「最も高い商品」を取りたい場合。
import java.util.Comparator;
import java.util.List;
public class MaxItemSample {
public static void main(String[] args) {
List<Item> items = List.of(
new Item("A", 100),
new Item("B", 250),
new Item("C", 180)
);
Item maxItem = items.stream()
.max(Comparator.comparingInt(Item::getPrice))
.orElse(null); // 空なら null
System.out.println(maxItem.getName()); // B
}
}
Javaここでの重要ポイントは二つです。
一つ目は、max(Comparator.comparingInt(Item::getPrice)) という一行が
「価格を基準に最大の要素を選ぶ」というルールを表していることです。
二つ目は、戻り値が Optional<Item> なので、orElse(null) や orElseThrow(...) で「空だったときの扱い」を決める必要があることです。
これもユーティリティにしてしまうと、呼び出し側が楽になります。
import java.util.Comparator;
import java.util.List;
public final class Maxes {
private Maxes() {}
public static <T> T maxOrNull(List<T> source, Comparator<? super T> comparator) {
if (source == null || source.isEmpty()) {
return null;
}
return source.stream()
.max(comparator)
.orElse(null);
}
}
Java使い方はこうです。
Item maxItem = Maxes.maxOrNull(items, Comparator.comparingInt(Item::getPrice));
Javanull を含むデータの最大値取得
「null を無視する」か「null を最小扱いにする」か
現実のデータには null が混ざります。
最大値取得で大事なのは、「null をどう扱うか」を決めることです。
例えば、「null は無視して最大値を取る」なら、こう書きます。
public static Integer maxIntIgnoreNull(List<Integer> source) {
if (source == null || source.isEmpty()) {
return null;
}
return source.stream()
.filter(v -> v != null)
.mapToInt(Integer::intValue)
.max()
.orElseThrow(); // null 以外が一つもなければ例外
}
Javaあるいは、「null は最小扱い(0など)にする」なら、mapToInt の前に変換してしまう方法もあります。
public static int maxIntTreatNullAsZero(List<Integer> source) {
if (source == null || source.isEmpty()) {
return 0;
}
return source.stream()
.mapToInt(v -> v == null ? 0 : v)
.max()
.orElse(0);
}
Javaここでの重要ポイントは、
「null の扱いも“最大値取得のルール”の一部」だと意識することです。
ユーティリティに閉じ込めておけば、
呼び出し側は「このメソッドは null をどう扱うのか」を意識せずに済みます。
まとめ:最大値取得ユーティリティで身につけてほしい感覚
最大値取得は、
単に「一番大きいものを探すテクニック」ではなく、
「空・null・オブジェクト・比較ルールを含めて、“最大”をどう定義するかをコードに刻む作業」です。
数値Listなら mapToInt(...).max().orElse(デフォルト) の形を基本として覚える。
オブジェクト一覧では、「どの項目を最大化するか」を関数(Item::getPrice など)で渡す汎用ユーティリティにする。
「最大値そのもの」だけでなく、「最大の要素そのもの」を max(Comparator...) で取得するパターンも押さえる。
null や空コレクションの扱い(0にする、nullを返す、例外にする)をユーティリティ側で統一する。
あなたのコードの中に、
毎回 for 文で最大値を手作業で探している箇所があるなら、
それを一度「最大値取得ユーティリティ+Stream」に置き換えられないか眺めてみてください。
その小さな整理が、
「“最大”という概念を、迷いなく安全に扱えるエンジニア」への、
確かな一歩になります。
