【Java入門】Stream APIについて

#目的

Java言語を含めたプログラミングの学習を始めたばかりの方、既学習者の方は復習用に、
今回は**Stream API**について学ぶために書いています。

[【Java入門目次】](https://qiita.com/morioheisei/items/6c7fc063c26e63d890ac)
・[変数と型](https://qiita.com/morioheisei/items/3d27e82d6cd88068dded)
・[型変換](https://qiita.com/morioheisei/items/28ab5fd69570e01cd317) 
・[変数のスコープ](https://qiita.com/morioheisei/items/c56ab32b68ce92d32206)
・[文字列の操作](https://qiita.com/morioheisei/items/e58adadd8535971b69be)
・[配列の操作](https://qiita.com/morioheisei/items/b6acbb1b0c07b40e7f56)
・[演算子](https://qiita.com/morioheisei/items/63088c61e8234dc1f7f5)
・[条件分岐](https://qiita.com/morioheisei/items/194cdc73a29bddade0a4)
・[繰り返し処理](https://qiita.com/morioheisei/items/e686e33b60c6a60e87f0)
・クラスについて（準備中）
・抽象クラス（準備中）
・インターフェース（準備中）
・カプセル化（準備中）
・モジュールについて（準備中）
・[例外処理について](https://qiita.com/morioheisei/items/d25d2b67e7530c1aab9d)
・[ラムダ式について](https://qiita.com/morioheisei/items/2bfcc3d8d8440b00e6a9)
・Stream APIについて ←今ここ

#Stream APIとは

Java8から導入された機能で、コレクションの要素に対する操作が可能なAPIです。

`コレクションをStream化→中間操作（データを加工）→終端操作（加工したデータを得る）`

という処理の流れになります。

Stream APIの中間操作は複数記述することが可能なので、複雑そうに見える処理もスッキリと記述することが出来ます。

各メソッドの引数に関数型インターフェースを受け取る場合もあり、そこでラムダ式を用いていますがそこの説明は割愛します。
**[ラムダ式について](https://qiita.com/morioheisei/items/2bfcc3d8d8440b00e6a9)**をご覧ください。


#Stream APIの例

実際にどの様に扱うのか簡単な例で比較します。

まずは、Stream APIを使わず実装。

```java:
List<String> names = Arrays.asList("大瀬良", "佐村河内", "菊池", "畝", "長野", "城之内");
for (int i = 0; i < names.size(); i++) {
  if (names.get(i).length() >= 3) {
    if (names.get(i).contains("内")) {
      System.out.println(names.get(i)); // 佐村河内 城之内
    }
  }
}
```

次に、`Stream APIを使って`同様の処理を実装します。


```java:StreamAPIを使用した実装
List<String> names = Arrays.asList("大瀬良", "佐村河内", "菊池", "畝", "長野", "城之内");
names.stream()
     .filter(name -> name.length() >= 3)
     .filter(name -> name.contains("内"))
     .forEach(name -> System.out.println(name)); // 佐村河内 城之内
```

ネストが深くならず読みやすくなり、シンプルに実装出来ていますね。

中間操作のfilterメソッドで条件を満たす要素のみを抽出し、
終端操作のforEachメソッドで1つずつ要素を出力しています。

#中間操作の紹介

先程は中間操作のメソッドとしてfilterを紹介しましたが、その他のメソッドもいくつか紹介していきます。

###1.map

各要素に指定した処理を行うメソッドです。

```java:mapメソッド
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
       .map(num -> num * 2) // 各要素を2倍にする
       .forEach(num -> System.out.print(num + " ")); // 2 4 6 8 10
```

###2.limit

指定した要素数（maxSize個）だけを返してくれるメソッドです。

```java:limitメソッド
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
numbers.stream()
       .limit(3) // 3個だけ取り出す
       .forEach(num -> System.out.print(num + " ")); // 1 2 3 
```

###3.distinct

重複を除いた要素を返してくれるメソッドです。

```java:distinctメソッド
List<String> names = Arrays.asList("久保", "遠藤", "巻", "久保", "岡崎", "本田", "巻", "遠藤");
names.stream()
     .distinct() // 重複した要素は取り除いて返す
     .forEach(name -> System.out.print(name + " ")); // 久保 遠藤 巻 岡崎 本田
```

###4.sorted

要素を並び替えてくれるメソッドです。

```java:sortedメソッド
List<Integer> numbers = Arrays.asList(1, 5, 3, 2, 4);
numbers.stream()
       .sorted() // 昇順に並び替え
       .forEach(num -> System.out.print(num + " ")); // 1 2 3 4 5
```

sortedメソッドの引数にComparatorインターフェースのreverseOrderメソッドを渡すことで、降順に並び替えることも出来ます。

```java:sortedメソッド
List<Integer> numbers = Arrays.asList(1, 5, 3, 2, 4);
numbers.stream()
       .sorted(Comparator.reverseOrder()) // 降順に並び替え
       .forEach(num -> System.out.print(num + " ")); // 5 4 3 2 1
```

#終端操作の紹介

これまでは終端操作のメソッドとしてforEachメソッドを用いていましたが、その他のメソッドもいくつか紹介していきます。

###1.anyMatch

条件によってbooleanを返すメソッドです。
要素の中に1つでも条件に合致する値があればtrueを返します。（一部一致判定）

```java:anyMatchメソッド
List<String> fruits = Arrays.asList("banana", "apple", "orange", "pineapple");
boolean result = fruits.stream()
                       .anyMatch(fruit -> fruit.length() >= 7); // 7文字以上の文字数の要素はあるか判定
System.out.println(result); // true
```

###2.allMatch

条件によってbooleanを返すメソッドです。
要素全てが条件に合致していればtrueを返します。（全部一致合致）

```java:allMatchメソッド
List<Integer> numbers = Arrays.asList(2, 4, 6, 7, 8, 10);
boolean result = numbers.stream()
                        .allMatch(num -> num % 2 == 0); // 要素全てが偶数であるかを判定している
System.out.println(result); // false
```

###3.noneMatch

条件によってbooleanを返すメソッドです。
要素の中に１つでも条件に合致するもがあればfalseを返します。（全て非合致判定）

```java:noneMatchメソッド
List<String> names = Arrays.asList("鈴木", "松井", "野茂", "井川", "川崎", "新庄", "ダルビッシュ有");
boolean result = names.stream()
                      .noneMatch(name -> name.length() > 4); // 4文字以上の文字数の要素はあるか判定
System.out.println(result); // false
```

#終わりに

コレクション要素に対しての操作を自分でダラダラと書くより、Stream APIを用いる事でソースコードがシンプルで読みやすくなるという事が分かりました。
最初は戸惑うかもしれませんが、ラムダ式と一緒にしっかりと覚えておきたいですね。



#参考サイト

**[Stream Oracle公式](https://docs.oracle.com/javase/jp/11/docs/api/java.base/java/util/stream/Stream.html#anyMatch(java.util.function.Predicate))**
**[【Java】Stream APIを書き続けて得た知見](https://www.unitrust.co.jp/8367)**