Java | Java 標準ライブラリ:Stream の生成

Java Java
スポンサーリンク

「Stream の生成」をざっくりイメージする

Stream API の最初の一歩は、「そもそも Stream をどうやって作るか」です。
filtermap は、その前に「Stream がある」ことが前提になります。

イメージとしては、

「元データ(List・配列・ファイルなど)から、“データの流れ”を取り出す」

という作業が「Stream の生成」です。

よく使うパターンから順に押さえていきましょう。
最初は「List から」「配列から」あたりを固めれば十分です。


コレクション(List / Set / Map)からの Stream 生成

List / Set から stream() を呼ぶ

いちばんよく使うのがこれです。
List や Set は java.util.Collection を実装していて、stream() メソッドを持っています。

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

public class FromList {
    public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("Alice");
        names.add("Bob");
        names.add("Carol");

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

        stream
            .filter(name -> name.length() <= 4)
            .forEach(System.out::println);
    }
}
Java

names.stream() の時点で、Stream<String> が生成されています。
そこに対して filterforEach をつなげている、という形です。

ここで大事なのは、「List に対して直接 for 文を回さず、“まず stream() で流れに変換する”」という感覚です。

補足として、Set<String> からも全く同じように set.stream() で生成できます。

Set<String> set = new HashSet<>();
// 何か追加して…
Stream<String> setStream = set.stream();
Java

Map からの Stream(entrySet / keySet / values を使う)

Map は、そのままでは stream() を持っていません。
キーだけ・値だけ・キーと値のペア、のどれをストリームにするかを自分で決めます。

import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;

public class FromMap {
    public static void main(String[] args) {
        Map<String, Integer> scores = new HashMap<>();
        scores.put("Alice", 80);
        scores.put("Bob",   65);
        scores.put("Carol", 90);

        Stream<Map.Entry<String, Integer>> entryStream =
                scores.entrySet().stream();

        entryStream
            .filter(e -> e.getValue() >= 70)
            .forEach(e -> System.out.println(e.getKey() + " : " + e.getValue()));
    }
}
Java

キーのストリームにしたければ scores.keySet().stream()
値だけなら scores.values().stream() という流れです。

「Map から Stream を作るときは、まず Set や Collection に変換してから」というパターンを覚えておくとスムーズです。


配列からの Stream 生成(Arrays.stream / Stream.of)

Arrays.stream(配列) を使う

配列から Stream を作る代表的な方法が Arrays.stream です。

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

public class FromArray {
    public static void main(String[] args) {
        String[] names = {"Alice", "Bob", "Carol"};

        Stream<String> stream = Arrays.stream(names);
        stream.forEach(System.out::println);

        int[] nums = {1, 2, 3, 4, 5};
        IntStream intStream = Arrays.stream(nums);

        int sum = intStream.sum();
        System.out.println(sum);  // 15
    }
}
Java

参照型(String[] など)の場合は Stream<T>
プリミティブ型(int[] など)の場合は IntStream などの専用ストリームになります。

ここでのポイントは、「配列 → Arrays.stream → Stream」というルートを体に入れておくことです。
List に変換してから list.stream() とするより、Arrays.stream のほうがシンプルで無駄も少ないです。

Stream.of(…) で“直接”作る

少数の値から簡単に Stream を作りたいときは Stream.of もよく使います。

import java.util.stream.Stream;

public class FromOf {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("Alice", "Bob", "Carol");

        stream
            .map(String::toUpperCase)
            .forEach(System.out::println);
    }
}
Java

テストコードやサンプルコードなど、「ちょっとだけ値を流したい」という場面で便利です。
Stream.of(array) のように書くと、一見配列を 1 要素として流してしまうので、
「配列から要素を流したいときは Arrays.stream を使う」と覚えておく方が安全です。


数値用のストリーム(IntStream / LongStream / DoubleStream)

mapToInt や Arrays.stream(int[]) で生成する

Stream<Integer> よりも、IntStream などのプリミティブ専用ストリームのほうが、
sumaverage などの数値系操作がしやすく、性能的にも有利なことが多いです。

配列からの例はすでに出しましたが、mapToInt から生成するパターンもよく使います。

import java.util.Arrays;

public class IntStreamExample {
    public static void main(String[] args) {
        String[] names = {"Alice", "Bob", "Carol"};

        int totalLength =
                Arrays.stream(names)            // Stream<String>
                      .mapToInt(String::length) // IntStream に変換
                      .sum();

        System.out.println(totalLength);        // 13
    }
}
Java

ここでは、

Stream<String>mapToIntIntStream を生成
IntStreamsum() で合計

という流れです。

頭の整理としては、

参照型のストリームから mapToInt / mapToLong / mapToDouble で数値ストリームを生成
あるいは、最初から Arrays.stream(int[]) などで数値ストリームを作る

と押さえておけば十分です。


ファイルや文字列など「外部データ」からの Stream 生成

Files.lines(Path) で“1 行ずつの Stream”を作る

ファイルを 1 行ずつ Stream として扱いたいときは Files.lines が定番です。

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;

public class FromFile {
    public static void main(String[] args) throws IOException {
        Path path = Path.of("sample.txt");

        try (Stream<String> lines = Files.lines(path)) {
            long count =
                    lines
                        .filter(line -> !line.isBlank())
                        .count();

            System.out.println("空行以外の行数: " + count);
        }
    }
}
Java

Files.lines はファイルの中身を全部読み込むのではなく、「行を順に読みながら流していく」イメージです。
try-with-resources を使って、使い終わったら自動的にクローズされるように書くのが基本形です。

Pattern.splitAsStream で“区切り文字ごとの Stream”を作る

文字列を正規表現で区切って Stream にしたいときは、PatternsplitAsStream を使う方法もあります。

import java.util.regex.Pattern;

public class FromStringSplit {
    public static void main(String[] args) {
        String text = "apple,banana,orange";

        Pattern.compile(",")
               .splitAsStream(text)
               .forEach(System.out::println);
    }
}
Java

「カンマ区切り」「スペース区切り」など、文字列の前処理でよく使います。


自分で Stream を「生み出す」パターン(generate / iterate / builder)

ここは少し応用寄りですが、「Stream の正体」に近づくところなので、イメージだけ掴んでおくと後で効きます。

Stream.generate で「無限ストリーム」を作る

Stream.generate は、「指定した Supplier を何度も呼び続けるストリーム」を生成します。

import java.util.UUID;
import java.util.stream.Stream;

public class GenerateExample {
    public static void main(String[] args) {
        Stream<String> randomIds =
                Stream.generate(() -> UUID.randomUUID().toString());

        randomIds
            .limit(3) // 無限に出続けるので、どこかで制限する
            .forEach(System.out::println);
    }
}
Java

generate で作ったストリームは「終わりがない」ので、
limit などでサイズを制限するのが必須です。

Stream.iterate で「次々と値を計算し続ける」ストリーム

iterate は「前の値から次の値を計算する」タイプのストリームです。

import java.util.stream.Stream;

public class IterateExample {
    public static void main(String[] args) {
        Stream<Integer> naturalNumbers =
                Stream.iterate(1, n -> n + 1);

        naturalNumbers
            .limit(5)
            .forEach(System.out::println); // 1, 2, 3, 4, 5
    }
}
Java

iterate(初期値, 次の値を計算する関数) という形で使います。
これも無限ストリームなので、limit などが必須です。

Stream.builder で「後から要素を追加していく」

要素を一つずつ積み上げて、最後に Stream として使いたい場合は Stream.builder もあります。

import java.util.stream.Stream;

public class BuilderExample {
    public static void main(String[] args) {
        Stream<String> stream =
                Stream.<String>builder()
                      .add("Alice")
                      .add("Bob")
                      .add("Carol")
                      .build();

        stream.forEach(System.out::println);
    }
}
Java

テストデータを柔軟に組み立てるときなどに便利ですが、
初心者の段階では「こういうのもあるんだ」くらいで構いません。


空の Stream と Optional からの Stream

Stream.empty() で「空のストリーム」

要素ゼロのストリームが欲しいときは Stream.empty() を使えます。

import java.util.stream.Stream;

public class EmptyExample {
    public static void main(String[] args) {
        Stream<String> empty = Stream.empty();

        long count = empty.count();  // 0
        System.out.println(count);
    }
}
Java

メソッドの戻り値として「要素がない場合は空の Stream を返す」という設計にすると、
呼び出し側で null チェックをせずに済むので、コードがすっきりします。

Optional からの Stream(少し応用)

Java 9 以降では、Optionalstream() メソッドがあります。

import java.util.Optional;

public class OptionalStreamExample {
    public static void main(String[] args) {
        Optional<String> maybeName = Optional.of("Alice");

        long count =
                maybeName.stream()
                         .filter(name -> name.startsWith("A"))
                         .count();

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

Optional.empty() の場合は「要素ゼロの Stream」として扱われるので、
Stream パイプラインに Optional を自然に混ぜ込むことができます。


まとめ:Stream の生成をどう頭に定着させるか

Stream の生成を初心者向けに整理すると、こうなります。

一番大事なのは、

  • List や Set → collection.stream()
  • 配列 → Arrays.stream(array)
  • 少数の値 → Stream.of(...)

この 3 つです。これだけでも、日常的なコードの 8 割くらいは賄えます。

そこに加えて、

  • Map → map.entrySet().stream() などにしてから
  • 数値系 → IntStream など(Arrays.stream(int[])mapToInt
  • ファイル → Files.lines(path)
  • 無限・計算系 → Stream.generate, Stream.iterate
  • 完全に空 → Stream.empty()

あたりを、「ああ、そういう入り口があるんだな」というレベルで覚えておくと、
「欲しいときに調べればすぐ書ける」状態になります。

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