Java Tips | コレクション:nullセーフStream

Java Java
スポンサーリンク

「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 は“何も起きない”だけで、例外にはならない」という性質です。
filtermapforEach も、要素がなければ何も実行されません。

三つ目は、「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);
Java

map.get("key") が null でも、空 Stream になるだけで安全に流せます。


まとめ:nullセーフStreamで身につけてほしい感覚

nullセーフStreamは、
単に「NPE を避ける小技」ではなく、
「null を“空の流れ”に変換して、処理の形を揃える設計」です。

from(Collection) で「null/空コレクション → 空 Stream」という入口を用意する。
配列や単一要素にも同じ考え方を広げて、fromArrayofNullable を用意する。
一度 Stream に乗せてしまえば、以降は null を気にせず filter / map / collect を書ける。
「null をどう扱うか」をチームの方針として決め、その方針をユーティリティに刻む。

あなたのコードのどこかに、
if (x != null) { x.stream() ... } が何度も出てきているなら、
それを一度「nullセーフStreamユーティリティ」に置き換えられないか眺めてみてください。

その小さな整理が、
「null に振り回されず、流れの形でロジックを書けるエンジニア」への、
確かな一歩になります。

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