Java8 StreamAPI


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

## Java8

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

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

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

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

## Lambda

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

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

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

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

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

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

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

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

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

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

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

## StreamAPI

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

1. listからジョンを取り除く
2. "hello XXX" な文章を作る.
3. 出力する.

```java: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を使って書くと、こう↓なります.

```java: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から生成できます.

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

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

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

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

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

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

## 値を処理する

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

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

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


## 値を変換する

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

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

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

## 要素を取り除く

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

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

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

## 個数を制限する

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

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

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

## ソートする

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

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

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

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

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

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

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

## リストにして返す

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

```java: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() ``` にします. それだけです.

```java: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() ```を追加するだけです.

```java: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について、ひと通り試して纏めたコードです.

* https://bitbucket.org/megmogmog1965/java8streamapi/src

## 参考にしたサイト

* [Java Streamメモ(Hishidama's Java8 Stream Memo)]
* [Java8のStreamを使いこなす(きしだのはてな)]



[Java Streamメモ(Hishidama's Java8 Stream Memo)]:http://www.ne.jp/asahi/hishidama/home/tech/java/stream.html
[Java8のStreamを使いこなす(きしだのはてな)]:http://d.hatena.ne.jp/nowokay/20130504