「試験対策とハンズオンでおぼえるラムダ式＆Stream API」を後日ひとりでやってみる

# 概要
[JavaDayTokyo2016 のセッション資料](http://www.oracle.com/technetwork/jp/ondemand/online2016-javaday-3003089-ja.html)が公開されました。当日参加できなかった「[試験対策とハンズオンでおぼえるラムダ式＆Stream API](http://www.oracle.co.jp/jdt2016/pdf/HO-1.pdf)」 の問題を今になって解いてみたので、記録を投稿してみます。

## リンク
1. [解答のコード(GitHub)](https://github.com/toastkidjp/Java8HandsOn)
2. [HandsOn5&6の解答に必要な Person.java](https://github.com/toastkidjp/Java8HandsOn/blob/master/src/main/java/jp/toastkid/java8handson/Person.java)

---

# 1. Runnable オブジェクトをラムダ式を使ってインスタンス化する

```java
Runnable r = new Runnable() {
    @Override
    public void run() {
        System.out.println("Hello Lambda !!");
    }
};
```

## Answer
Runnable オブジェクトを直接 run していますが、動作確認のためなので気にしないでください。

```java:Answer
public class HandsOn1 {
    public static void main(final String[] args) {
        final Runnable r = () -> System.out.println("Hello Lambda !!");
        r.run();
    }
}
```

```:実行結果
Hello Lambda !!
```

---

# 2. MyFunction (関数型インタフェース)を引数にとる print メソッドを実装する

```java
private void print(int a, int b, MyFunction f) {
    System.out.println(f.calc(a, b));
}
```

## Answer

```java:Answer
public class HandsOn2 {

    public static void main(final String[] args) {
        print(10, 2, (a, b) -> 10 + 2);

    }

    private static void print(final int a, final int b, final MyFunction f) {
        System.out.println(f.calc(a, b));
    }

    private interface MyFunction {
        int calc(int a, int b);
    }
}
```

```:実行結果
12
```

## java.util.function
| Interface | Description  | Abstract method | Example
|:---|:---|:---|:---
| Function | 受け取った引数を変換 | R apply (T t) | Stream.map [ほか|https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/class-use/Function.html]
| Consumer | 受け取った引数を操作して終了 | void accept(T t) | Stream.forEach [ほか|https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/class-use/Consumer.html]
| Supplier | 新しく値を生成 | T get() | ThreadLocal.withInitial [ほか|https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/class-use/Supplier.html]
| UnaryOperator | 引数と同じ型の値を返却 | T apply(T t) | Stream.iterate [ほか|https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/class-use/UnaryOperator.html]
| Predicate | 引数で論理演算して真偽を返却 | boolean test(T t) | Stream.filter [ほか|https://docs.oracle.com/javase/jp/8/docs/api/java/util/function/class-use/Predicate.html]

---

# 3. 関数型インタフェイスを使う

1. get() で任意の文字列を返す s を Supplier で実装
2. 引数を Sysout するメソッド accept を持つ c を Consumer で実装
3. 2つの引数の和を返すメソッド apply を持つ bf を BiFunction で実装

```java:Answer
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;

public class HandsOn3 {

    public static void main(final String[] args) {
        first();
        second();
        third();
    }

    /**
     * 引数なし.
     * get() で任意の文字列を返す s を Supplier で実装
     */
    private static void first() {
        final Supplier<String> s = () -> "tomato";
        System.out.println(s.get());
    }

    /**
     * 引数1つ.
     * 引数を Sysout するメソッド accept を持つ c を Consumer で実装
     */
    private static void second() {
        final Consumer<Integer> c = System.out::println;
        c.accept(25);
    }

    /**
     * 引数2つ.
     * 2つの引数の和を返すメソッド apply を持つ bf を BiFunction で実装
     */
    private static void third() {
        final BiFunction<Integer, Integer, Integer> bf = (a, b) -> a + b;
        System.out.println(bf.apply(1, 2));
    }

}
```

```:実行結果
tomato
25
3
```

---

# 4.FizzBuzz を IntStream で実装する
三項演算子を駆使するとかなりシンプルに書けるそうですが、今回はやりませんでした。

```java:Answer
import java.util.stream.IntStream;

public class HandsOn4 {

    public static void main(final String[] args) {
        fizzBuzz(100);
    }

    /**
     * 整数 n までの FizzBuzz を出力する.
     * @param n
     */
    private static void fizzBuzz(final int n) {
        IntStream.range(1, n + 1).forEach(i -> {
            if (1 < i) {
                System.out.print(", ");
            }
            if (i % 15 == 0) {
                System.out.print("FizzBuzz");
                return;
            }
            if (i % 3 == 0) {
                System.out.print("Fizz");
                return;
            }
            if (i % 5 == 0) {
                System.out.print("Buzz");
                return;
            }
            System.out.print(i);
        });
    }
}
```

```:実行結果
1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, FizzBuzz, 16, 17, Fizz, 19, Buzz, Fizz, 22, 23, Fizz, Buzz, 26, Fizz, 28, 29, FizzBuzz, 31, 32, Fizz, 34, Buzz, Fizz, 37, 38, Fizz, Buzz, 41, Fizz, 43, 44, FizzBuzz, 46, 47, Fizz, 49, Buzz, Fizz, 52, 53, Fizz, Buzz, 56, Fizz, 58, 59, FizzBuzz, 61, 62, Fizz, 64, Buzz, Fizz, 67, 68, Fizz, Buzz, 71, Fizz, 73, 74, FizzBuzz, 76, 77, Fizz, 79, Buzz, Fizz, 82, 83, Fizz, Buzz, 86, Fizz, 88, 89, FizzBuzz, 91, 92, Fizz, 94, Buzz, Fizz, 97, 98, Fizz, Buzz
```

## Stream クラスの中間操作メソッド
| Method   | Description
|:---------|:---
| filter   | Stream に残す要素を Predicate で指定
| map      | 要素を Function で変形
| flatMap  | 要素が配列ないし Collection なら、その要素で置き換え
| distinct | 重複要素を除外
| peek     | Consumer で操作をし、 Stream を返す。デバッグ等で使用
| limit    | 指定した数の要素を残す

## Stream クラスの終端操作メソッド
| Method    | Description
|:---|:---
| forEach   | 各要素に対し、Consumer で同一の処理を実行
| reduce    | BinaryOperator で reduce
| collect   | Stream を Collection に変換、Collectors クラスを使うことが多い
| min       | Stream の最小値を返す。sort 方法は Comparator で指定
| max       | Stream の最大値を返す。sort 方法は Comparator で指定
| count     | Stream の要素数を返す
| findFirst | Stream の最初の要素を Optional で返す
| findAny   | Stream の任意の要素を Optional で返す
| anyMatch  | Predicate の条件に1つ以上一致する or Stream が空なら true
| allMatch  | Predicate の条件に全要素が一致する or Stream が空なら true
| noneMatch | Predicate の条件に全要素が一致しない or Stream が空なら true

---

# 5 と 6 の下準備
Person クラスを作ります。

```java:Person.java
import java.util.Arrays;
import java.util.List;

public class Person {

    private enum Gender {MALE, FEMALE};

    private final String firstName;
    private final String lastName;
    private final int    age;
    private final Gender gender;

    public static class Builder {
        private String firstName;
        private String lastName;
        private int    age;
        private Gender gender;

        public Builder setFirstName(final String firstName) {
            this.firstName = firstName;
            return this;
        }

        public Builder setLastName(final String lastName) {
            this.lastName = lastName;
            return this;
        }

        public Builder setAge(final int age) {
            this.age = age;
            return this;
        }

        public Builder setGender(final Gender gender) {
            this.gender = gender;
            return this;
        }

        public Person build() {
            return new Person(this);
        }
    }

    public Person(final Builder b) {
        this.firstName = b.firstName;
        this.lastName  = b.lastName;
        this.age       = b.age;
        this.gender    = b.gender;
    }

    public String getFirstName() {
        return firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public int getAge() {
        return age;
    }

    public Gender getGender() {
        return gender;
    }

    public static List<Person> PEOPLE = Arrays.asList(
        new Person.Builder().setFirstName("太郎").setLastName("山田").setAge(21)
            .setGender(Gender.MALE).build(),
        new Person.Builder().setFirstName("花子").setLastName("佐藤").setAge(25)
            .setGender(Gender.FEMALE).build(),
        new Person.Builder().setFirstName("次郎").setLastName("青山").setAge(45)
            .setGender(Gender.MALE).build(),
        new Person.Builder().setFirstName("アン").setLastName("港").setAge(85)
            .setGender(Gender.FEMALE).build(),
        new Person.Builder().setFirstName("三郎").setLastName("鈴木").setAge(25)
            .setGender(Gender.MALE).build(),
        new Person.Builder().setFirstName("四郎").setLastName("伊藤").setAge(67)
            .setGender(Gender.MALE).build(),
        new Person.Builder().setFirstName("五郎").setLastName("工藤").setAge(55)
            .setGender(Gender.MALE).build()
        );
}
```

---

# 5. 年齢が 50 より小さい Person オブジェクトの名前(LastName+ FirstName) を表示する

```java:Answer
public class HandsOn5 {
    public static void main(final String[] args) {
        Person.PEOPLE.stream()
            .filter(person -> person.getAge() < 50)
            .map(   person -> person.getLastName() + person.getFirstName())
            .forEach(System.out::println);
    }
}
```

```:実行結果
山田太郎
佐藤花子
青山次郎
鈴木三郎
```

---

# 6. Person を年齢で昇順ソートし、名前(LastName + FirstName) をカンマ区切りで連結した文字列を取得する

```java:Answer
import java.util.stream.Collectors;

public class HandsOn6 {
    public static void main(final String[] args) {
        System.out.println(
            Person.PEOPLE.stream()
                .sorted((p1, p2) ->Integer.compare(p1.getAge(), p2.getAge()))
                .map(    person -> person.getLastName() + person.getFirstName())
                .collect(Collectors.joining(", "))
                );
    }
}
```

```:実行結果
山田太郎, 佐藤花子, 鈴木三郎, 青山次郎, 工藤五郎, 伊藤四郎, 港アン
```

---

# 7.整数値リストの101以上200未満の要素を合計する

```java:Answer
import java.util.Arrays;
import java.util.List;

public class HandsOn7 {
    public static void main(final String[] args) {
        final List<Integer> games = Arrays.asList(118, 65, 152, 201, 126);
        final int totalScore = games.stream()
                .filter(i -> 100 < i)
                .filter(i -> i < 200)
                .mapToInt(i -> i)
                .sum();
        System.out.println(totalScore);
    }
}
```

```:実行結果
396
```

ちなみに、こう書いても同じ結果になります。

```java:Answer2
        final int totalScore = games.stream()
                .filter(i -> 100 < i)
                .filter(i -> i < 200)
                .collect(Collectors.summingInt(i -> i));
```

ただし、問題の指示では reduce を使うようにとのことですので、書き直します。

```java:Answer3
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

public class HandsOn7 {
    public static void main(final String[] args) {
        final List<Integer> games = Arrays.asList(118, 65, 152, 201, 126);
        final Optional<Integer> totalScore = games.stream()
                .filter(i -> 100 < i)
                .filter(i -> i < 200)
                .reduce((a, b) -> a += b);
        System.out.println(totalScore.get());
    }
}
```

今回、 reduce を初めて使いました。