PR

【Java】Stream APIとは?filter・map・collectの使い方を初心者向けに1から解説

Java Stream APIの概念を示すアイキャッチ画像 Java

JavaのStream APIは、ひとことで言うと、Listなどのデータを「順番に加工する流れ」として書くための仕組みです。

たとえば、名簿の中から「Sで始まる名前だけ」を取り出して、「大文字」に変えて、「新しいList」にしたいとします。このような処理を、上から順番に読める形で書けるのがStream APIです。

最初から難しい用語を覚える必要はありません。まずは「データを流して、途中で絞り込みや変換をして、最後に結果を受け取る」と考えればOKです。

この記事では、Stream APIの意味、for文との違い、filtermapcollectの読み方を、初心者向けにできるだけかみ砕いて解説します。

なお、この記事で扱うStreamは、java.util.streamのStream APIです。ファイルの読み書きで出てくるInputStreamOutputStreamとは別物です。ファイル入出力のストリームは、Java.ioを使用したファイルの読み書きで解説しています。

スポンサーリンク

まず結論:Stream APIは「データを順番に加工する書き方」

Stream APIを理解するときは、次の3つだけを先に押さえてください。

  • 元になるデータを用意する
  • 途中で、必要なものだけ残したり、形を変えたりする
  • 最後に、Listなどの結果として受け取る

この流れをコードで書くと、だいたい次の形になります。

元のデータ.stream()
        .途中の処理()
        .途中の処理()
        .最後に結果を受け取る処理();

専門用語では、途中の処理を「中間操作」、最後の処理を「終端操作」と呼びます。ただ、初心者のうちは、まず「途中の処理」と「最後の処理」と考えれば十分です。

Stream APIはListそのものではなくListを処理するための流れを作るという概念図
Streamは「データを入れる箱」ではなく、Listを処理するための「流れ」です。

何に困ったときにStream APIを使うの?

Stream APIは、複数のデータをまとめて扱うときによく使います。

たとえば、次のような場面です。

  • Listの中から条件に合うデータだけ取り出したい
  • Listの中身を別の形に変換したい
  • 条件に合うデータが何件あるか数えたい
  • 最初の1件だけ取り出したい

つまりStream APIは、「データの一覧をどう加工するか」を書くときに便利です。

まだListMapがあやふやな場合は、先にJavaのListやMapの使い方を読むと理解しやすくなります。

まずはこのコードを読めればOK

Stream APIの基本は、次のコードでかなりつかめます。

List<String> result = names.stream()
        .filter(name -> name.startsWith("S"))
        .map(name -> name.toUpperCase())
        .collect(Collectors.toList());

このコードは、次のように読みます。

コードやっていること
names.stream()namesというListを処理の流れに乗せる
filter(...)条件に合う名前だけ残す
map(...)残った名前を大文字に変える
collect(...)最後にListとして受け取る

この読み方ができれば、最初の一歩としては十分です。

for文で書く場合と比べてみる

Stream APIでできることは、for文でも書けます。まず、for文で書くと次のようになります。

for文とStream APIの違いを初心者向けに比較した概念図
for文は手順を1つずつ書く、Stream APIは目的を流れで書く、と考えると整理しやすくなります。
List<String> result = new ArrayList<>();

for (String name : names) {
    if (name.startsWith("S")) {
        result.add(name.toUpperCase());
    }
}

これはこれで分かりやすい書き方です。ただし、処理の流れを読むには、resultを作って、for文で回して、if文で判定して、addする、という手順を追う必要があります。

同じ処理をStream APIで書くと、次のようになります。

List<String> result = names.stream()
        .filter(name -> name.startsWith("S"))
        .map(name -> name.toUpperCase())
        .collect(Collectors.toList());

Stream APIでは、「何をしたいのか」が上から順番に並ぶため、慣れると処理の目的を読み取りやすくなります。

Stream APIは「for文より偉い書き方」ではありません。データを絞り込み、変換し、結果を作る流れを見せたいときに便利な書き方です。

filter・map・collectを1つずつ理解する

初心者が最初に覚えるべきなのは、filtermapcollectの3つです。

Stream APIでまず覚えるfilter map collectの3つの処理を示した図
まずは、filterで残す、mapで変える、collectで受け取る、の3つだけ押さえましょう。

filterは「条件に合うものだけ残す」

filterは、条件に合うデータだけを残す処理です。

.filter(name -> name.startsWith("S"))

この例では、「Sで始まる名前だけ残す」という意味です。条件に合わないTanakaのような名前は、次の処理へ進みません。

mapは「別の形に変える」

mapは、データを別の形に変える処理です。

.map(name -> name.toUpperCase())

この例では、名前を大文字に変えています。SatoSATOになります。

collectは「最後に結果を受け取る」

collectは、途中で処理した結果を最後に受け取るための処理です。

.collect(Collectors.toList())

この例では、処理結果を新しいListとして受け取っています。

ここで出てくるname -> ...の部分はラムダ式です。ラムダ式が分からない場合は、Javaのラムダ式の記事をあわせて読むと理解しやすくなります。

実際に動くサンプルコード

全体のコードは次の通りです。

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class StreamSample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Sato", "Suzuki", "Tanaka", "Sasaki");

        List<String> result = names.stream()
                .filter(name -> name.startsWith("S"))
                .map(name -> name.toUpperCase())
                .collect(Collectors.toList());

        System.out.println(result);
    }
}

実行すると、次のように表示されます。

[SATO, SUZUKI, SASAKI]

TanakaはSで始まらないため、filterの時点で除外されています。

初心者がつまずきやすいポイント

Stream APIで最初につまずきやすいポイントを整理します。

つまずきまずこう理解する
StreamはListなの?Listそのものではなく、Listを処理するための流れ
filterだけで結果が出る?最後にcollectやcountなどが必要
mapとforEachは同じ?mapは変換、forEachは最後に1件ずつ処理
parallelStreamは速い?必ず速いわけではない。最初はstreamで十分

特に大事なのは、Streamは元のListを直接書き換えるものではないという点です。

次のコードでは、大文字にした結果はresultに入ります。元のnamesがそのまま大文字に変わるわけではありません。

List<String> result = names.stream()
        .map(name -> name.toUpperCase())
        .collect(Collectors.toList());

また、findFirst()のようなメソッドではOptionalが出てくることがあります。Optionalについては、JavaのOptionalとは?で解説しています。

Stream APIはいつ使う?いつ使わない?

Stream APIは、次のような処理に向いています。

  • Listから条件に合うものだけ取り出す
  • データを別の形に変換する
  • 件数を数える
  • 処理の流れを短く、読みやすく見せたい

一方で、処理の途中で複雑な分岐が何度も出てくる場合や、for文の方が素直に読める場合は、無理にStream APIを使う必要はありません。

Stream APIは、コードを短くするためだけの道具ではありません。データをどう加工して、最後に何を得たいのかを見やすくするための道具です。

関連して読みたい記事

Stream APIは、Javaの他の基礎知識とつながっています。

まとめ

JavaのStream APIは、Listなどのデータを「処理の流れ」として書くための仕組みです。

初心者のうちは、まず次の3つだけ覚えてください。

  • filterは、条件に合うものだけ残す
  • mapは、別の形に変える
  • collectは、最後に結果を受け取る

最初から難しいメソッドを全部覚える必要はありません。まずは、names.stream().filter(...).map(...).collect(...)という流れを読めるようになればOKです。

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