Java Streamについてのまとめ


Stream便利だなーと思ったので、まとめてみます。

## Streamの作成

### Of
Streamを返す。
要素に対しての型指定も可能。

```java
// 直接作成
Stream stream = Stream.of("a", "b", "c", "d", "e");
IntStream intStream = IntStream.of(1, 2, 3, 4, 5);
DoubleStream doubleStream = DoubleStream.of(1.0, 2.0, 3.0, 4.0, 5.0);
LongStream longStream = LongStream.of(1, 2, 3, 4, 5);
```

### 変換
主要な型からstreamに変換。

```java
// Listから変換
List<String> list = Arrays.asList("a", "b", "c", "d", "e");
Stream listStrem = list.stream();

// Mapから変換
Map<Integer, String> map = new HashMap<>();
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
Stream<Map.Entry<Integer, String>> mapStream = map.entrySet().stream();

// 文字列から変換
String str = "ABCDE";
IntStream strStream = str.chars();

```
      
### Builder
Streamのビルダーを返す。

```java
Stream.Builder<Integer> sb = Stream.builder();
sb.add(10);
sb.add(20);
Stream<Integer> buildStream =sb.build();
buildStream.forEach(System.out::println);
```

```console
10
20
```

### Generate
順序付けされていない無限順次ストリームを返す。

```java
Stream<Integer> stream = Stream.generate(() -> 1);
stream.limit(3).forEach(System.out::println);
```

```console
1
1
1
```

ランダムな値を生成する場合などに使いたい。
例:0~9までのランダムな値を５個返す。

```java
Stream.generate(Math::random).limit(5).map(s -> Math.floor(s * 10)).mapToInt(Double::intValue).forEach(System.out::println);
```

```console
2
2
9
6
8
```

### Iterate

順序付けされた無限順次Streamを返す。

```java
Stream<Integer> stream = Stream.iterate(1, x -> x * 2);
        stream.limit(5).forEach(System.out::println);
```

```console
1
2
4
8
16
```

### concat
Streamを結合する。

```java
Stream<String> stream1 = Stream.of("a", "b");
Stream<String> stream2 = Stream.of("c", "d", "e");
Stream.concat(stream1,stream2).forEach(System.out::println);
```

```console
a
b
c
d
e
```

### empty
空のStreamを作成。

```java
Stream<?> emptyList =  Stream.empty();
emptyList.forEach(System.out::println);
```

```console
No OutPut
```

## 要素の取得

ラムダ式やメソッド参照を使用するとスッキリ書ける。
文字列に関しては、戻り型が`IntStream型`のため`mapToObj`メソッドで型変換を行う必要がある。

```java

        // Stream型
        // ラムダ式
        Stream.of("a", "b", "c", "d", "e").forEach(s -> System.out.println(s));
        // メソッド参照
        Stream.of("a", "b", "c", "d", "e").forEach(System.out::println);

        //List
        Arrays.asList(1, 2, 3, 4, 5).stream().forEach(System.out::println);

        //Map
        Map<Integer, String> map = new HashMap<>();
        map.put(1, "A");
        map.put(2, "B");
        map.put(3, "C");
        map.entrySet().stream().forEach(s -> System.out.println("key: " + s.getKey() + " value: " + s.getValue()));

        //文字列
        String str = "ABCDE";
        str.chars().mapToObj(s -> (char)s)
                .forEach(System.out::println);
```

## 数値範囲

### range

```java
// 末尾を含まない
IntStream.range(1,5).forEach(System.out::println);
```
```console
1
2
3
4
```
###rangeClosed

```java
// 末尾を含む
IntStream.rangeClosed(1,5).forEach(System.out::println);
```
```console
1
2
3
4
5
```

## 中間操作

## 要素を変換する
### map
ラムダで処理を書ける。

```java
Stream.of(1, 2, 3, 4, 5).map(s -> s * 2).forEach(System.out::println);
```

```console
2
4
6
8
10
```

### mapToInt
`mapToLong`,`mapToDouble`も使い方は同じ。

```java
Stream.of("1", "2", "3", "4", "5")  // Stream<String>
        .mapToInt(s -> Integer.parseInt(s)) // IntStream
        .map(s -> s * 2)
        .forEach(System.out::println);
```

```console
2
4
6
8
10
```

### flatMap
1対Nで変換できる。

```java
// flatMap
Stream<String> stream1 = Stream.of("A", "BB", "CCC", "DDDD", "EEEEE");
stream1.flatMap(s -> Stream.of(s, s.length(), s.toLowerCase())).forEach(System.out::print);
```

```console
A1aBB2bbCCC3cccDDDD4ddddEEEEE5eeeee
```

## 要素を絞り込む
### filter
```java        
Stream.of(10, 20, 30, 40, 50).filter(s -> s <= 30).forEach(System.out::println);
```

```console
10
20
30
```

### skip & limit
**skip**

```java
Stream.of(10, 20, 30, 40, 50).skip(2).forEach(System.out::println);
```

```console
30
40
50
```
**limit**

```java        
Stream.of(10, 20, 30, 40, 50).limit(2).forEach(System.out::println);
```

```console
10
20
```
**skip & limit**
`skip`と`limit`を組み合わせることで範囲を指定できる。

```java
Stream.of(10, 20, 30, 40, 50).skip(2).limit(2).forEach(System.out::println);
```

```console
30
40
```

### distinct
重複を取り除く。

```java        
Stream.of("A", "A", "B", "B", "B", "C").distinct().forEach(System.out::println);
```

```console
A
B
C
```

## 要素を並べ換える

### sorted

```java
// sorted
Stream.of(13, 52, 34, 6, 28).sorted((s1, s2) -> s1 - s2).forEach(System.out::println);
// Comparatorオブジェクトを使用
Stream.of(13, 52, 34, 6, 28).sorted(Comparator.comparingInt(s -> s)).forEach(System.out::println);
```

```console
6
13
28
34
52
```

## デバック
### peek

デバック出力用のメソッド。streamに関して何も影響を及ぼさない。

```java
List<String> numbers =  Stream.of("one", "two", "three", "four")
                        .filter(e -> e.length() > 3)
                        .peek(e -> System.out.println("Filtered value: " + e))
                        .map(String::toUpperCase)
                        .peek(e -> System.out.println("Mapped value: " + e))
                        .collect(Collectors.toList());
```

```console
Filtered value: three
Mapped value: THREE
Filtered value: four
Mapped value: FOUR
```


## 終端操作

## 結果を出力する
### forEach
繰り返し処理を行う。ラムダ式を用いて、操作を書くことができる。
下の例を書いて見たが、mapで書き直せる...。

```java
Stream.of(10, 20, 30, 40 ,50).forEach(s -> {
    int intResult = s / 10;
    System.out.println(intResult);
});
```

### forEachOrdered
並列処理を行う際に順序通りに出力できる。`forEach`を使用するとランダムな順序で出力される。
`parallelStream()`は並列処理用のstream。

```java
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
System.out.println("--------forEach---------");
list.parallelStream().filter(s -> s > 1).forEach(System.out::println);
System.out.println("--------forEachOrdered---------");
list.parallelStream().filter(s -> s > 1).forEachOrdered(System.out::println);
```
forEachはランダムに出力されるのに対し、forEachOrderedはリストの順序通りに出力される。

```console
--------forEach---------
3
5
4
2
--------forEachOrdered---------
2
3
4
5
```


## 結果を取り出す
### collect
様々な型で結果を返す。

**list**

```java
// list型で返却
List<String> list = Stream.of("A", "B", "C", "D", "E").collect(Collectors.toList());
System.out.println(list);
```

```console
[A, B, C, D, E]
```

**String**

```java
//String型で返却
String join1 = Stream.of("A", "B", "C", "D", "E").collect(Collectors.joining());
System.out.println(join1);

String join2 = Stream.of("A", "B", "C", "D", "E").collect(Collectors.joining(","));
System.out.println(join2);
```

```console
ABCDE
A,B,C,D,E
```

**Map**

Group化したものをMapで返却してくれる。


```Score.java
//最低限のモデルクラス
public class Score {
    private String name;
    private int score;

    public Score(String name, int score){
        this.name = name;
        this.score = score;
    }

    public String getName() {
        return name;
    }

    public int getScore() {
        return score;
    }
}
```
```java
// scoreListを作成
List<Score> scoreList = new ArrayList<>();
scoreList.add(new Score("Tanaka", 90));
scoreList.add(new Score("Suzuki", 80));
scoreList.add(new Score("Nakamura", 100));
scoreList.add(new Score("Ito", 90));
scoreList.add(new Score("Hamanaka", 100));
//グループ分け
Map<Integer, List<Score>> map = scoreList.stream().collect(Collectors.groupingBy(Score::getScore));
//Mapの要素を出力
map.entrySet().stream().forEach(s -> System.out.println("Key: " + s.getKey() + ", Value:" + s.getValue()));
//scoreが100点のスコアデータを表示
List<Score> score100 = map.get(100);
score100.forEach(s -> System.out.println("name: " + s.getName() + ", score: " + s.getScore()));
```

返却されたMapはグループ化されて、Valueに対応するStreamがリスト化されている。

```console
# Mapの要素を出力
Key: 80, Value:[com.example.javalearn.qiita.stream.Score@446cdf90]
Key: 100, Value:[com.example.javalearn.qiita.stream.Score@799f7e29, com.example.javalearn.qiita.stream.Score@4b85612c]
Key: 90, Value:[com.example.javalearn.qiita.stream.Score@277050dc, com.example.javalearn.qiita.stream.Score@5c29bfd]
# scoreが100点のスコアデータを表示
name: Nakamura, score: 100
name: Hamanaka, score: 100
```

### toArray

Streamを**配列**に変換する。

```java
String[] toArray = Stream.of("a", "b", "c", "d", "e").toArray(String[]::new);
for(String s:toArray){
    System.out.println(s);
}
```

```console
a
b
c
d
e
```

### reduce
結果を累積する。

```java
int total = Stream.of(1, 2, 3, 4, 5).reduce((a,b) -> a + b).get();
System.out.println(total);

//メソッド参照も可能
int total1 = Stream.of(1, 2, 3, 4, 5).reduce(Integer::sum).get();
```

やっていることとしては、以下のようなことである。

```console
1 + 2 = 3
3 + 3 = 6
6 + 4 = 10
10 + 5 = 15
```

```console
15
```

## 結果を絞り込む
### findFirst
リストの最初の要素を取り出す。

```java
String findFirst = Stream.of("A", "B", "C", "D", "E").findFirst().orElse("null");
System.out.println(findFirst);
```

```console
A
```

### max

```java
Integer max = Stream.of(1, 2, 3, 4, 5).max(Comparator.naturalOrder()).orElse(0);
System.out.println(max);
```

```console
5
```

### min

```java
Integer min = Stream.of(1, 2, 3, 4, 5).min(Comparator.naturalOrder()).orElse(0);
System.out.println(min);
```

```console
1
```

### sum

```java
Integer sum = IntStream.of(1, 2, 3, 4, 5).sum();
System.out.println(sum);
```

```console
15
```

### count

```java
Long count = Stream.of(1, 2, 3, 4, 5).count();
System.out.println(count);
```

```console
5
```

### average

```java
Double average = IntStream.of(1, 2, 3, 4, 5).average().orElse(0);
System.out.println(average);
```

```console
3.0
```

## 結果を検索する

### anyMatch

部分一致

```java
Boolean anyMatch1 = Stream.of(1, 2, 3, 4, 5).anyMatch(s -> s > 2);
Boolean anyMatch2 = Stream.of(1, 2, 3, 4, 5).anyMatch(s -> s < 0);
System.out.println(anyMatch1);
System.out.println(anyMatch2);
```

```console
true
false
```

### allMatch

完全一致

```java
Boolean allMatch1 = Stream.of("one", "two", "three", "four").allMatch(s -> s.length() <= 5);
Boolean allMatch2 = Stream.of("one", "two", "three", "four").allMatch(s -> s.length() <= 4);
System.out.println(allMatch1);
System.out.println(allMatch2);
```

```console
true
false
```

### noneMatch

完全不一致

```java
Boolean noneMatch1 = Stream.of("one", "two", "three", "four").noneMatch(s -> s.length() <= 2);
Boolean noneMatch2 = Stream.of("one", "two", "three", "four").noneMatch(s -> s.length() <= 4);
System.out.println(noneMatch1);
System.out.println(noneMatch2);
```

```console
true
false
```






## 参考

[Stream (Java Platform SE 8 )](https://docs.oracle.com/javase/jp/8/docs/api/java/util/stream/Stream.html)