LoginSignup
53
54

More than 5 years have passed since last update.

Java8 StreamAPI

Last updated at Posted at 2015-02-08

今更ですが、Java8 (特にStreamAPI) について試したので、まとめ。

Java8

Java8で追加・変更された主な機能は以下の3つ。

  • Lambda
  • StreamAPI
  • Join/Fork (Java7〜)

この3つの機能はお互い関連しあっていて、LambdaはStreamAPIを簡潔に書けるようにするし、Join/Forkは、StreamAPIを介して ( .parallel()とかで) 簡単に使えるようになっている。

ということで、メインはStreamAPIについてですが、Lambda、Join/Forkの話も簡単に書きます。

Lambda

Consumer<String>というClassを無名newして、accept(String t)をOverrideしています.
このコードをLambdaを使って簡潔に書いてみます.

original
Stream.of("Taro", "Hanako", "John")
        .forEach(new Consumer<String>() {

    @Override
    public void accept(String t) {
        System.out.println(t);
    }
});

まず、Classの宣言とメソッド名までを消せます.

クラスの省略
Stream.of("Taro", "Hanako", "John")
        .forEach((String t) -> {
        System.out.println(t);
});

引数の型も消せます. 一個しか無い時は括弧も消せます.

引数の省略
Stream.of("Taro", "Hanako", "John")
        .forEach(t -> {
        System.out.println(t);
});

本文が1行しか無い時は、括弧とセミコロン, Returnを消せます.

本文の省略
Stream.of("Taro", "Hanako", "John")
        .forEach(t -> System.out.println(t));

値をメソッドにそのまま渡すだけの場合、メソッド参照に書き換えられます.

本文の省略
Stream.of("Taro", "Hanako", "John")
        .forEach(System.out::println);

StreamAPI

StreamAPIとは、 (乱暴な言い方をすると) 拡張for文を置き換える ものです.
例えば以下のような、listをforeachして加工&出力するコードがあります。

  1. listからジョンを取り除く
  2. "hello XXX" な文章を作る.
  3. 出力する.
original
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

// (1) listからジョンを取り除く
List<String> list2 = new ArrayList<>();
for(String name : list1) {
    if(!name.equals("John")) {
        list2.add(name);
    }
}

// (2) "hello XXX" な文章を作る.
List<String> list3 = new ArrayList<>();
for(String name : list2) {
    String sentence = "hello " + name;
    list3.add(sentence);
}

// (3) 出力する.
for(String sentence : list3) {
    System.out.println(sentence);
}

StreamAPIを使って書くと、こう↓なります.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.stream()
        .filter(s -> !s.equals("John"))
        .map(s -> "hello " + s)
        .forEach(System.out::println);

コードが簡潔になって見やすくなるのもメリットですが、一番重要なのは、 実行ステップ行が減って、バグも比例して少なくなる という事だと思います.

Java8以降は、for文を使ったコードはレガシーコードとなって、Gotoみたいに「使ったらカッコ悪いコード」となる日が来るのかもしれません...

Stream class

先ほどの例で、 .filter, .map, .forEach等のメソッド呼び出しをしていますが、これらは全てStreamクラスのメソッドです.
Streamクラスの生成の仕方にはいくつか方法があります.

List, Setから生成できます.

List<String> list1 = Arrays.asList("Taro", "Hanako", "John");
Stream<String> stream = list1.stream();

Stream.ofで直接生成できます.

Stream<String> stream = Stream.of("aaa", "bbb", "ccc");

数値の場合、範囲指定で生成できます.

IntStream stream = IntStream.range(1, 5);

Streamのサブクラスには、IntStream, LongStream, DoubleStream等があります.

値を処理する

リストの要素を全て順番に出力してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.stream()
        .forEach(System.out::println);

値を変換する

リストの要素に "hello " をつけて (変換) 、出力してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.stream()
        .map(s -> "hello " + s)
        .forEach(System.out::println);

要素を取り除く

リストの"John"要素を取り除いて、出力してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.stream()
        .filter(s -> !s.equals("John"))
        .forEach(System.out::println);

個数を制限する

リストの要素を2つだけ、出力してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.stream()
        .limit(2)
        .forEach(System.out::println);

ソートする

リストの要素をソートして、出力してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.stream()
        .sorted()
        .forEach(System.out::println);

条件を満たす要素があるか判定する

リストの要素に"Hanako"があるか、判定してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

boolean exist = list1.stream()
        .anyMatch(s -> s.equals("Hanako"));

リストにして返す

Streamをリストにして返してみます.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

List<String> list2 = list1.stream()
        .collect(Collectors.toList());

.parallel (Join/Fork)

StreamAPIを使うと、並列化プログラミングが簡単にできます.
今まで list.stream() だった所を list.parallelStream() にします. それだけです.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.parallelStream()
        .map(s -> "hello" + s)
        .sorted()
        .forEach(System.out::println);

実行してみるが、ソートされていない...

Taro
John
Hanako

出力もMulti-threadで処理されているからですね.
出力部分だけ直列に戻します. .sequential()を追加するだけです.

StreamAPI
List<String> list1 = Arrays.asList("Taro", "Hanako", "John");

list1.parallelStream()
        .map(s -> "hello" + s)
        .sorted()
        .sequential()
        .forEach(System.out::println);

ちゃんと順番に表示されます.

Hanako
John
Taro

サンプルコード

ここで述べたもの以外にも、大量の便利なAPIがあります.
StreamAPIについて、ひと通り試して纏めたコードです.

参考にしたサイト

53
54
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
53
54