Java Tips | コレクション:Stream生成

Java Java
スポンサーリンク
スポンサーリンク

Stream生成は「データに“流れ”をつける」入り口

Stream は「コレクションや配列などの要素を、流れとして扱うためのビュー」です。
filter や map、collect などの“Stream 操作”を使うためには、まず「Stream をどうやって作るか(生成するか)」を押さえる必要があります。

業務コードでは、ほぼ次のパターンから Stream を作ります。
コレクション(List・Set)から
配列から
単発の値や可変長引数から
数値レンジや乱数などの“連番・無限列”から

ここを整理しておくと、「とりあえず Stream にしてから考える」という書き方ができるようになります。


コレクションからの Stream生成

List・Set からは stream() が基本

Collection(List・Set など)には、stream() メソッドが用意されています。

import java.util.List;
import java.util.stream.Stream;

public class FromCollection {

    public static void main(String[] args) {
        List<String> names = List.of("山田", "佐藤", "鈴木");

        Stream<String> stream = names.stream();

        stream
            .filter(n -> n.startsWith("佐"))
            .forEach(System.out::println); // 佐藤
    }
}
Java

ここでの重要ポイントは、stream() が「元データをコピーするのではなく、“ビュー”を作るだけ」ということです。
Stream 自体は“使い捨て”で、一度終端操作(forEach や collect など)を呼ぶと再利用できません。

「List で持っておく → 必要なときに list.stream() で流れを作る」というスタイルを基本形として覚えてください。


配列からの Stream生成

参照型配列:Arrays.stream と Stream.of

String[] などの参照型配列から Stream を作る代表的な方法は二つあります。

import java.util.Arrays;
import java.util.stream.Stream;

public class FromArrayRef {

    public static void main(String[] args) {
        String[] array = {"A", "B", "C"};

        Stream<String> s1 = Arrays.stream(array);
        Stream<String> s2 = Stream.of(array);

        s1.forEach(System.out::println); // A B C
    }
}
Java

参照型配列に関しては、Arrays.stream も Stream.of も「要素ごとの Stream」を返すので、挙動はほぼ同じです。

ただし、「“配列そのもの”を 1 要素として扱いたい」場合は Stream.of(array)(型は Stream<String[]>)という使い方もあるため、
「配列の中身を流したいのか」「配列自体を流したいのか」を意識して選ぶのが大事です。

プリミティブ配列:Arrays.stream 一択で覚える

int[] などのプリミティブ配列は、Arrays.stream を使うのが基本です。

import java.util.Arrays;
import java.util.stream.IntStream;

public class FromArrayPrimitive {

    public static void main(String[] args) {
        int[] nums = {1, 2, 3};

        IntStream stream = Arrays.stream(nums);

        int sum = stream.sum();
        System.out.println(sum); // 6
    }
}
Java

ここでの重要ポイントは二つです。
Arrays.stream(int[]) は IntStream を返すので、sum や average など“数値専用の便利メソッド”が使える。
Stream.of(nums) だと Stream<int[]> になってしまい、「配列1個が1要素」になってしまうので、配列の中身を処理したいときには不向き。

「配列の要素を処理したいなら、プリミティブでも参照型でも、とりあえず Arrays.stream」と覚えておくと安全です。


単発値・可変長引数からの Stream生成

Stream.of で「その場でちょっとした Stream」を作る

「わざわざ List を作るほどでもないけれど、Stream で書きたい」というときは、Stream.of が便利です。

import java.util.stream.Stream;

public class FromValues {

    public static void main(String[] args) {
        Stream<String> stream =
                Stream.of("山田", "佐藤", "鈴木");

        long count =
                stream.filter(n -> n.length() == 2)
                      .count();

        System.out.println(count);
    }
}
Java

ここでの重要ポイントは、Stream.of が「引数に渡した値を、そのまま順番付きの Stream にしてくれる」ことです。
テストコードや、ちょっとした分岐処理などで「3〜4個の候補を Stream で処理したい」ときに、とても書きやすくなります。


数値レンジ・無限列からの Stream生成

IntStream.range / rangeClosed で「連番の Stream」

ループの代わりに「1〜N までの連番」を Stream で扱いたいときは、IntStream.range 系を使います。

import java.util.stream.IntStream;

public class FromRange {

    public static void main(String[] args) {
        IntStream.range(0, 5)          // 0,1,2,3,4
                 .forEach(System.out::println);

        IntStream.rangeClosed(1, 3)    // 1,2,3
                 .forEach(System.out::println);
    }
}
Java

range(開始, 終了) は「開始以上、終了未満」、
rangeClosed(開始, 終了) は「開始以上、終了以下」です。

for ループの代わりに「インデックスの Stream」を作るイメージで使うと、
mapToObj(i -> ...) などと組み合わせて柔軟な処理が書けるようになります。

Stream.generate / iterate で「無限列」

「同じ値を繰り返す」「前の値から次の値を計算する」といった“無限列”も Stream で作れます。

import java.util.stream.Stream;

public class FromInfinite {

    public static void main(String[] args) {
        Stream.generate(() -> "A")
              .limit(3)
              .forEach(System.out::println); // A A A

        Stream.iterate(1, n -> n + 1)
              .limit(5)
              .forEach(System.out::println); // 1 2 3 4 5
    }
}
Java

ここでの重要ポイントは、「必ず limit などで打ち切ること」です。
無限列は強力ですが、終端操作までに“どこかで止める”ことを忘れると、延々と処理が続いてしまいます。


Stream生成ユーティリティで「入口の迷い」を減らす

「とりあえず Stream にする」ための小さなヘルパー

実務では、「null かもしれないコレクション」「配列かもしれないし単体かもしれない値」など、
“そもそも何から Stream を作るか”で毎回 if 文を書きがちです。

そういうときは、入口だけユーティリティに寄せてしまうとスッキリします。

import java.util.Collection;
import java.util.stream.Stream;

public final class Streams {

    private Streams() {}

    public static <T> Stream<T> from(Collection<T> c) {
        if (c == null || c.isEmpty()) {
            return Stream.empty();
        }
        return c.stream();
    }

    @SafeVarargs
    public static <T> Stream<T> ofNullable(T... values) {
        if (values == null || values.length == 0) {
            return Stream.empty();
        }
        return Stream.of(values);
    }
}
Java

使い方のイメージです。

Streams.from(list)
       .filter(...)
       .forEach(...);

Streams.ofNullable(maybeValue)
       .filter(...)
       .forEach(...);
Java

「null なら空 Stream」「そうでなければ普通の Stream」という入口を一箇所に閉じ込めることで、
呼び出し側は「とにかく Stream として扱う」ことだけに集中できます。


まとめ:Stream生成で身につけてほしい感覚

Stream生成は、
単に「書き方を暗記する」話ではなく、
「データの“持ち方”と“流し方”を分けて考えるための入口設計」です。

コレクションなら collection.stream()、
参照型配列なら Arrays.stream(array)、
プリミティブ配列なら Arrays.stream(primitiveArray)、
単発値なら Stream.of(...)、
連番なら IntStream.range / rangeClosed、
無限列なら Stream.generate / iterate。

このあたりを“手癖”で選べるようになると、
「まず Stream にしてから、filter / map / collect を考える」という書き方が自然にできるようになります。

あなたのコードのどこかに、
同じような for ループが何度も出てきているなら、
一度「その入口を Stream生成に置き換えられないか」を眺めてみてください。

その小さな一歩が、
「データ処理を“流れ”として設計できるエンジニア」への、
確かなステップになります。

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