Java | 実務で避けるべき Stream のアンチパターン集

Java Java
スポンサーリンク

ここでは 実務でよくある Stream API のアンチパターン集 を、初心者でも理解できるように「やってはいけない例 → 理由 → 改善例」のセットでまとめます。


1. 副作用を持つ forEach の乱用

❌ NG例

List<String> list = List.of("apple", "banana", "cherry");
List<String> result = new ArrayList<>();

list.stream().forEach(s -> result.add(s.toUpperCase()));
Java
  • 問題点
    • Stream の利点である「宣言的・副作用なし」が失われる
    • 並列処理(parallelStream())にすると不具合が出やすい
  • 改善例
List<String> result = list.stream()
    .map(String::toUpperCase)
    .toList();
Java

✅ map → collect に置き換えることで副作用ゼロ、並列化も安全


2. Stream 内で例外を投げる

❌ NG例

List<String> list = List.of("1", "2", "a", "4");
list.stream()
    .map(Integer::parseInt)  // "a" があると NumberFormatException
    .forEach(System.out::println);
Java
  • 問題点
    • Stream は遅延実行で例外がどこで起きるか分かりにくい
    • 全体処理が止まってしまう
  • 改善例
List<Integer> nums = list.stream()
    .map(s -> {
        try {
            return Integer.parseInt(s);
        } catch (NumberFormatException e) {
            return null;
        }
    })
    .filter(Objects::nonNull)
    .toList();
Java

✅ 例外をハンドリングして安全に流せる


3. 重複した .stream() 呼び出し

❌ NG例

List<String> list = List.of("a", "b", "c");
long count = list.stream().count();
long nonEmpty = list.stream().filter(s -> !s.isEmpty()).count();
Java
  • 問題点
    • 同じリストに対して複数回 Stream を作る → 冗長・性能悪化
  • 改善例
long nonEmptyCount = list.stream()
    .filter(s -> !s.isEmpty())
    .count();
Java

✅ 1つの Stream でまとめて処理


4. 不要な parallelStream() の多用

❌ NG例

List<Integer> list = List.of(1,2,3,4,5);
list.parallelStream()
    .forEach(System.out::println);
Java
  • 問題点
    • 小規模データだと逆に遅くなる
    • 順序保証がない場合がある(println の順番が乱れる)
  • 改善例
list.stream()
    .forEach(System.out::println);
Java

✅ 並列化は大量データでのみ使用、順序を意識する場合は注意


5. ネストした Stream の乱用

❌ NG例

List<List<String>> data = List.of(
    List.of("A", "B"),
    List.of("C", "D")
);

data.stream()
    .map(inner -> inner.stream()
                      .map(String::toLowerCase)
                      .toList())
    .toList();
Java
  • 問題点
    • map 内で stream().toList() → ネストされた Stream が残る
    • 可読性・保守性が低下
  • 改善例
List<String> flat = data.stream()
    .flatMap(List::stream)
    .map(String::toLowerCase)
    .toList();
Java

✅ flatMap でフラット化してから処理するとスッキリ


6. 遅延実行の認識不足

❌ NG例

Stream<String> stream = list.stream()
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.length() > 3;
    });

// println を期待しても出力されない
Java
  • 問題点
    • Stream は中間操作(filter, mapなど)は遅延実行
    • 末端操作(collect, forEach)が呼ばれるまで実行されない
  • 改善例
list.stream()
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.length() > 3;
    })
    .toList();
Java

✅ 末端操作を呼ぶことで意図通りに実行される


まとめ:実務で意識すべきルール

アンチパターン改善のコツ
forEach で副作用map → collect に変える
Stream 内で例外try/catch で安全に処理
同じリストで複数 Stream1つにまとめる
parallelStream の乱用大規模データのみ、順序注意
ネストした StreamflatMap を使ってフラット化
遅延実行を無視必ず末端操作で Stream を確定

💡 実務の心得

  • Stream は「宣言的で副作用なし」を意識
  • 可読性・安全性を優先
  • 小規模処理は無理に Stream にせず拡張 for でOK
Java
スポンサーリンク
シェアする
@lifehackerをフォローする
スポンサーリンク
タイトルとURLをコピーしました