Java-StreamAPI-黒本で間違えた問題を理解する

### 前置き
JavaGold資格取得のため、黒本４章でStreamAPIで主が間違えた問題を勉強していきます。
今回は自分のためのほぼメモなので読めたもんじゃないです。

> 問　OptionalクラスのifPresentOrElseTestメソッドに渡す引数の型の組み合わせとして正しいものを選べ

「型の組み合わせ？知るか！」
「ifPresentOrElseメソッドじゃなくて？ifPresentOrElse"Test"メソッド!?」

...解説見たら一応納得できた
以下サンプルコード。

```java
import java.util.Optional;

public class ifPresentOrElseTest {
    public static void main(String[] args) {
        Optional<String> sample = Optional.empty();
        sample.ifPresentOrElse(
            (str) -> System.out.println(str), //値がある場合、ラムダ式(Consumer型)
            () -> System.out.println("empty");//値がない場合、ラムダ式(Runnable型）
    }
}
```

#### 答え
Consumer,Runnable
※結局Oracleの公式ドキュメントでもifPresentOrElseメソッドしかなく、
  ifPresentOrElseTestメソッドはありませんでした...黒本のミス？

「信じられるのは公式ドキュメントだら」

>String型要素を扱うjava.util.List型変数「list」から並列ストリームを取得するコードとして正しいものを選びなさい。

「並列ストリーム！？初耳ですけど！？」

以下、サンプルコード
```java
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

public class ParallelTest {
    public static void main(String[] args) {
        List<String> list = Arrays.asList("A","B","C","D","E");
        Stream<String> stream = list.parallelStream();
        stream.forEach(System.out::println);
    }
}
```
実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3484672/e008a326-a5ed-f52c-1a55-b86635c0765e.png)

「なるほど、parallelStreamを使うだけで並列で処理してくれるってことね」

>問　次のプログラムをコンパイル、実行したときの結果として、正しいものを選びなさい
```java
public class Value {
    private String data;
    public Value(String data) {
        this.data = data;
    }
    @Override
    public int hashCode() {
        return 100;
    }
    @Override 
    public boolean equals(Object obj) {
        return true;
    }
}
```
```java
import java.util.Arrays;
import java.util.List;

public class Test {
    public static void main(String[] args){
        List<Value> list = Arrays.asList(
            new Value("A"),
            new Value("B"),
            new Value("C"),
            new Value("A")
        };
        long size = list.stream().distinct().count();
        System.out.println(size);
    }
}
```
### 解答
「1」と表示される

「distinctでList重複なしで抽出しているから3か.....え！？"1"！？」

> 重複しているかどうかはequalsメソッドの結果がtrueになるかどうかによって判定されます

「あーValueクラスでequalsメソッドをオーバーライドして常にtrueを返すようにしているからか」
「ん？じゃあ、オーバーライドするのはequalsメソッドだけでhashCodeメソッドもする必要あるのか？」

オーバーライドするのをequalsメソッドだけ、またはhashCodeメソッドだけにしたところ、結果は"4"で重複を除くことはできませんでした...なんで？」

公式ドキュメントを見てみました
> 通常、このメソッドをオーバーライドする場合は、hashCodeメソッドを常にオーバーライドして、「等価なオブジェクトは等価なハッシュ・コードを保持する必要がある」というhashCodeメソッドの汎用規約に従う必要があることに留意してください。

「つまりequalsメソッドはhashCoddeメソッドで同じhashCodeであるかも見ているってことか..」

>問　次のプログラムの「/* insert code here */」に入るコードとして、最も適切なものを選びなさい
```java
List<Integer> list = Arrays.asList(1,2,3,4,5)
/* insert code here */ result = list.stream().reduce(0,(a,b) -> a + b);
```

「reduceメソッドはたしかOptional<T>型を返すだら？」
「答えはOptional<String>だ！」

#### 解答
int

「...え！？！？」
>なお初期値を受け取らないredduceメソッドの戻り値型はOptional<T>でしたが、この初期値を受け取るタイプのreduceメソッドの戻り値型は、初期値として渡したデータ型と同じになります

「....くそが！」

資格勉強ってこういう細かいところも覚えなきゃいけなくてつらいですよね

あ、ちなみに実行結果は順番に足されて行って15となります。

>問　java.util.stream.Collectorインターフェースに規定されているメソッドの内、実際に行いたい処理を記述するためのものを選びなさい

「Collectorインターフェースがそもそもわからないのですが...?」

###Collectorインターフェースを実現したクラスの定義
>Collectorには、supplier,accumulator,combiner,finisher,characteristicsという５つの抽象メソッドがあり、これらすべてを実装しなければなりません。

「え！？全部！？めんどくさ！」「こんな面倒くさいのいつ使うんだよ...」
| メソッド | 目的 |
|:-:|:-:|
| supplier  |  処理途中の値を保持するためのオブジェクトを生成するメソッド |
| accumurator  |  具体的に実行したい処理を記述したBiConsumer型のラムダ式を戻すメソッド |
|  combiner | 並列処理をしているときにこのに作られた処理途中の値を保持するためのオブジェクトを結合する  |
| finisher  | 処理結果を戻すラムダ式を提供するメソッド  |
| characteristics  | Collecoterの特徴を表すEnumのセットを戻すメソッド  |

「うーん、よくわからない。特にcombinerとcharacteristics。」

### 「とりあえず、サンプルコード実行して理解してみる」
```java
import java.util.EnumSet;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

public class SampleCollector implements Collector<String, StringBuilder, String> {

	@Override
	public Supplier<StringBuilder> supplier() { //処理途中を値をStringBuilder型で保持する
		return StringBuilder::new;
	}

	@Override
	public BiConsumer<StringBuilder, String> accumulator() { //具体的に実行したい操作
		return (builder, str) -> {
			if(builder.length() != 0) {
				builder.append(",");
			}
			builder.append(str);  //カンマを付けてStringBuilderに格納する
		};
	}

	@Override
	public BinaryOperator<StringBuilder> combiner() {
		return null;  
	}

	@Override
	public Function<StringBuilder, String> finisher() {//処理結果を戻す
		return builder -> builder.toString();  //StringBuilderを受け取りStringに変換して返す
	}

	@Override
	public Set<Characteristics> characteristics() {
		return EnumSet.noneOf(Characteristics.class);
	}
	
}
```
「いや、サンプル長すぎじゃん！」

とりあえず、combinerは置いといて、「characteristics」について
Collectorの特徴を表すEnumのセットを戻すメソッドらしい


【Collector.Characteristicsの列挙子】
| 列挙子 | 概要 |
|:-:|:-:|
| CONCURRENT  | このCollectorが並行処理をすることを表す  |
| IDDENTITY_FINISH  | このCollectorのfinisherメソッドが省略可能であることを表す  |
| UNORDERED  | コレクションの操作において順序の維持を保証しないことを表す  |

>もしこれらを指定する必要がなければ、java.util.EnumSetクラスのnoneOfメソッドにCollector.Characteristicsクラスのクラスリテラル（クラスの定義情報を表すリテラル）を渡せば、Collector.Characteristics型を扱う空のsetオブジェクトが生成されます

「結局よくわからんな。とりあえず深みにはまる前にCollectorインターフェースを実現したクラスの必ず実装しなければならないメソッドに"characteristics"という長くて読みにくいメソッドがある。このメソッドはCollectorの特徴を表すEnumのセットを戻すけど、よくわからない人は以下のように書いておけばいい」と覚えていったん置いておこう

```java
@Override 
public Set<Characteristics> characteristics() {
    return EnumSet.noneOf(Characteristics.class);
}
```

「combinerも並列処理の時に考えなければいけないメソッドとだけ覚えておこう」

「...え？適当すぎないかって？」
「資格勉強の第一目標は受かることだから深みにハマる前に先に進むのです！」

一旦ここまで