プログラミング

Java Stream API: mapを使ったデータ変換完全ガイド

この記事は約11分で読めます。

はじめに

Java の Stream API は、大量のデータを効率的に処理するための便利な機能を提供します。その中でも map メソッドは、各要素を別の型に変換する中間操作を簡潔に実現します。

本記事では、map メソッドの基本的な使い方から応用例までを、コード例を交えながら詳しく解説します。さらに、flatMap メソッドとの違いや使い分けについても学びます。

mapとは

map は Stream API の中間操作で、各要素を別の要素に変換するために使用されます。これにより、データを加工したり、新しい型に変換する処理が簡単に記述できます。例えば、以下のような用途があります。

  • データの変換
  • オブジェクトのプロパティ抽出

基本的なmapの使用例

比較簡単な例から見ていきましょう。

文字列を大文字に変換

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

public class MapExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("alice", "bob", "charlie");

        // 大文字に変換
        List<String> upperCaseNames = names.stream()
                .map(String::toUpperCase)
                .collect(Collectors.toList());

        System.out.println("Upper Case Names: " + upperCaseNames); 
        // 実行結果: 
        // [ALICE, BOB, CHARLIE]
    }
}

数値を倍に変換

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

public class MapExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // 数値を倍に変換
        List<Integer> doubledNumbers = numbers.stream()
                .map(n -> n * 2)
                .collect(Collectors.toList());

        System.out.println("Doubled Numbers: " + doubledNumbers);
        // 実行結果:
        // [2, 4, 6, 8, 10]
    }
}

mapの基本概念

map は Stream API のメソッドの一つで、Function (関数型インターフェース) を参照として受け取り、それを適用した要素で構成される Stream を返します。

Functionとは

Function は、たった1つの基本メソッド apply を持つ関数型インターフェースです。この apply メソッドは参数を受け、その参数に依存した返り値を返します。主にラムダ式の関数定義を通じて実装されます。以下は、Function の基本例です。

import java.util.function.Function;

public class FunctionExample {
    public static void main(String[] args) {
        Function<Integer, String> intToString = n -> "Number: " + n;
        System.out.println(intToString.apply(5));
        // 実行結果:
        // Number: 5
    }
}

詳しくは、関数型インターフェースに関する記事を確認してみてください!

map で Function を利用する例

以下の例は、数値のリストの各要素の数値を倍に変換するものです。

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

public class MapExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // map で数値を倍に変換
        List<Integer> doubledNumbers = numbers.stream()
                .map(n -> n * 2)
                .collect(Collectors.toList());

        System.out.println("Doubled Numbers: " + doubledNumbers);
        // 実行結果:
        // Doubled Numbers: [2, 4, 6, 8, 10]
    }
}

Functionを別作することで、同じ条件を複数の場所で再利用できる為、コードの再利用性を高めることができます。Functionを作らずとも、別メソッドを用意しそれを利用することもできます。

ラムダ式のメソッド参照を利用する例

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

public class MapExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // メソッド参照を使用して数値を倍に変換
        List<Integer> doubledNumbers = numbers.stream()
                .map(MapExample::toDouble)
                .collect(Collectors.toList());

        System.out.println("Doubled Numbers: " + doubledNumbers);
    }

    // メソッド参照先
    public static Integer toDouble(Integer number) {
        return number * 2;
    }
}

map と似た中間操作にflatMapというものがあります。参考までに、mapflatMapの違いについて解説していきます。

mapとflatMapの違い

map と似た中間操作にflatMapというものがあります。これらの違いは、以下のとおりです。

操作入力データ構造出力データ構造
mapStream<T>Stream<R>
flatMapStream<Stream<T>>Stream<T>
  • map は個別の Stream を返しますが、それらを連続して一つのStreamに組み合わせません。
  • flatMap はすべての Stream を返した後、それらを連続して一つの Stream にまとめます。

それぞれの使い方を例を使って解説します。

mapの例

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

public class MapVsFlatMap {
    public static void main(String[] args) {
        List<List<String>> listOfLists = Arrays.asList(
            Arrays.asList("a", "b"),
            Arrays.asList("c", "d"),
            Arrays.asList("e", "f")
        );

        // map を使用
        List<Stream<String>> mapped = listOfLists.stream()
                .map(List::stream)
                .collect(Collectors.toList());

        System.out.println("Mapped: " + mapped); 
        // 実行結果:
        // [java.util.stream.ReferencePipeline$Head@xxx, ...]
    }
}

“a”, “b”のStream、 “c”, “d”のStream、”e”, “f”のStreamを格納しているList情報が出力されてます。多次元配列のイメージです。

flatMapの例

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

public class MapVsFlatMap {
    public static void main(String[] args) {
        List<List<String>> listOfLists = Arrays.asList(
            Arrays.asList("a", "b"),
            Arrays.asList("c", "d"),
            Arrays.asList("e", "f")
        );

        // flatMap を使用
        List<String> flattened = listOfLists.stream()
                .flatMap(List::stream)
                .collect(Collectors.toList());

        System.out.println("Flattened: " + flattened);
        // 実行結果:
        // [a, b, c, d, e, f]
    }
}

mapでは多次元配列のようなものが返ってきていましたが、ここでは1つのList情報が出力されています。

flatMapの使い所:

  • ネストしたリストや配列の平坦化
  • 非同期処理の結果を統合
  • データの分解

以下は、文字列をスペースで分割して平坦化する例です。

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

public class FlatMapExample {
    public static void main(String[] args) {
        List<String> sentences = Arrays.asList(
                "Hello World",
                "Java Stream API",
                "FlatMap Example"
        );

        // 各文字列をスペースで分割し、平坦化
        List<String> words = sentences.stream()
                .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
                .collect(Collectors.toList());

        System.out.println("Words: " + words);
        // 実行結果:
        // [Hello, World, Java, Stream, API, FlatMap, Example]
    }
}

まとめ

  • map メソッド:
    ・Stream 内の各要素を 1:1 で変換する際に使用します。
    ・主な用途はデータ変換、プロパティ抽出、計算処理など。
  • flatMap メソッド:
    ・各要素を複数の要素に変換し、それらを平坦化します。
    ・ネストされたリストや非同期処理の結果を統合する場合に便利です。
  • 活用のポイント:
    適切なメソッド参照やラムダ式を用いることでコードを簡潔にできます。

Stream API のこれらの操作を活用することで、より簡潔かつ効率的なデータ操作が可能になります。ぜひコードに取り入れてみてください!