「nullセーフStream」は「nullかもしれないコレクションを、そのままStreamで扱う」技
業務コードでは、本当は良くないけれど、現実として「null が返ってくるコレクション」がたくさんあります。
検索結果が null のことがある
外部ライブラリが「要素なし=null」で返してくる
古いコードが null を前提に書かれている
こういうときに素直に list.stream() と書くと、即 NullPointerException です。
そこで使うのが「nullセーフStream」——「null なら空の Stream を返す」という小さなユーティリティです。
なぜ必要か:毎回 if (list != null) を書きたくない
本来なら「コレクションは null にしない」が理想ですが、
既存コードや外部APIの都合で、どうしても null を相手にしなければならない場面があります。
素直に書くと、こうなります。
List<String> names = findNames(); // null かもしれない
if (names != null) {
names.stream()
.filter(n -> n.length() >= 2)
.forEach(System.out::println);
}
Javaこれを毎回書くのはダルいし、
「if を書き忘れて NPE」が簡単に起きます。
ここで欲しいのは、
「とりあえず Stream にしたい。null なら“空の Stream”でいい」
という入口です。
基本形:null を空 Stream に変えるユーティリティ
Collection 用の from メソッド
一番よく使うのは、「Collection から null セーフに Stream を作る」メソッドです。
import java.util.Collection;
import java.util.stream.Stream;
public final class NullSafeStreams {
private NullSafeStreams() {}
public static <T> Stream<T> from(Collection<T> c) {
if (c == null || c.isEmpty()) {
return Stream.empty();
}
return c.stream();
}
}
Java使い方はこうなります。
List<String> names = findNames(); // null かもしれない
NullSafeStreams.from(names)
.filter(n -> n.length() >= 2)
.forEach(System.out::println);
Javaここでの重要ポイントは三つです。
一つ目は、「null でも空でも、とにかく Stream.empty() を返す」ことです。
呼び出し側は「とりあえず Stream として扱う」ことだけ考えればよく、null チェックを毎回書かなくて済みます。
二つ目は、「空 Stream は“何も起きない”だけで、例外にはならない」という性質です。filter も map も forEach も、要素がなければ何も実行されません。
三つ目は、「null を“特別扱いしない”という方針を、ユーティリティに閉じ込めている」ことです。
“要素がない”という意味では、null も空コレクションも同じ扱いにしてしまう、という割り切りです。
配列や単一要素にも広げる
配列版:null や長さ0を空 Stream にする
配列も同じように扱いたいなら、こう書けます。
import java.util.Arrays;
import java.util.stream.Stream;
public static <T> Stream<T> fromArray(T[] array) {
if (array == null || array.length == 0) {
return Stream.empty();
}
return Arrays.stream(array);
}
Java使い方の例です。
String[] array = findArray(); // null かも
NullSafeStreams.fromArray(array)
.map(String::toUpperCase)
.forEach(System.out::println);
Java単一要素版:null なら空、非null なら1要素 Stream
「1件だけかもしれない値」を Stream で扱いたいときもあります。
public static <T> Stream<T> ofNullable(T value) {
if (value == null) {
return Stream.empty();
}
return Stream.of(value);
}
Java使い方の例です。
User user = findUser(); // 見つからなければ null
NullSafeStreams.ofNullable(user)
.map(User::getName)
.forEach(System.out::println);
Javaここでの重要ポイントは、
「“あるかもしれない1件”を、Stream の世界に自然に持ち込める」ことです。
if (user != null) を書かずに、他の Stream 処理と同じ形で扱えます。
nullセーフStreamの設計で意識したいこと
「null を許すかどうか」を“入口で決める」
nullセーフStreamを導入するとき、一番大事なのは
「このプロジェクトでは、“Stream の入口で null を吸収する”」
という方針を決めることです。
入口で null を空 Stream に変える
→ 以降の処理は「null を気にしないで書ける」世界になる
逆に、
「null が来たらそれはバグだから、早めに落としたい」
という方針なら、あえて null セーフにしない選択もあります。
どちらが正しい、ではなく、
「このコードベースでは null をどう扱うか」をチームで揃えることが大事です。
よくある具体例
例1:検索結果の List が null のことがある
List<Order> orders = dao.findOrdersOrNull(customerId);
NullSafeStreams.from(orders)
.filter(o -> o.isActive())
.forEach(this::processOrder);
Javaここでのポイントは、「DAO が null を返すかどうか」を呼び出し側が意識しなくてよくなることです。
例2:Map#get の結果を Stream で扱う
Map<String, List<String>> map = ...;
NullSafeStreams.from(map.get("key"))
.forEach(System.out::println);
Javamap.get("key") が null でも、空 Stream になるだけで安全に流せます。
まとめ:nullセーフStreamで身につけてほしい感覚
nullセーフStreamは、
単に「NPE を避ける小技」ではなく、
「null を“空の流れ”に変換して、処理の形を揃える設計」です。
from(Collection) で「null/空コレクション → 空 Stream」という入口を用意する。
配列や単一要素にも同じ考え方を広げて、fromArray や ofNullable を用意する。
一度 Stream に乗せてしまえば、以降は null を気にせず filter / map / collect を書ける。
「null をどう扱うか」をチームの方針として決め、その方針をユーティリティに刻む。
あなたのコードのどこかに、if (x != null) { x.stream() ... } が何度も出てきているなら、
それを一度「nullセーフStreamユーティリティ」に置き換えられないか眺めてみてください。
その小さな整理が、
「null に振り回されず、流れの形でロジックを書けるエンジニア」への、
確かな一歩になります。
