mike-neckのブログ

JavaかJavaFXかJavaEE(なんかJava8が多め)

Java8 Streamでバリバリやれるようになりたい人のためのFunctional Interfaceまとめ

こんにちわ、みけです。

もう、Streamの書きすぎで、

なんだか、for文が書けない身体になってしまいました(大袈裟)


というわけで、Streamなのですが、

慣れていないと

「はよ!Supplier<?>BinaryConsumer<?, ?>をはよ!」

IDEにせかされて、

あれっ?これってなんだっけ?ってなってしまう

のではないかと思います。

というわけで、Streamで使う用途で

Functional Interfaceをまとめてみることにしました。


Streamの生成(source)に用いるもの

Supplier<T>

引数なしで、オブジェクトを生成するFunctional Interfaceです。

Stream#generateメソッドで利用します。

    @Test
    public void supplier_Tのサンプル () {
        final AtomicInteger count = new AtomicInteger(65);
        Supplier<Character> supplier = () -> (char)count.getAndIncrement();

        assertThat(supplier.get(), is('A'));
    }

Tintlongdoubleになっている

  • IntSupplier
  • LongSupplier
  • DoubleSupplier

というのもあり、

それぞれ、

  • IntStream#generate
  • LongStream#generate
  • DoubleStream#generate

で利用されます。


UnaryOperator<T>

T型の引数を1つ受け取り、T型のオブジェクトを返すFunctional Interfaceです。

Stream#iterateメソッドの第二引数で指定します。

    @Test
    public void unaryOperator_Tのサンプル () {
        UnaryOperator<Character> operator = (c) -> (char)((int)c + 1);

        char character = 'A';
        assertThat(operator.apply(character), is('B'));
    }

なお、intlongdouble用のUnaryOperator

IntUnaryOperatorなどがあります(後述)。


中間処理(intermediate operation)に用いるもの

Function<T, R>

T型の引数を1つ受け取り、R型のオブジェクトを返すFunctional Interfaceです。

Stream#mapメソッドで利用します。

    @Test
    public void function_T_Rのサンプル () {
        Function<String[], String> function = (array) -> array[1];

        String[] line = new String[]{"1", "みけ", "mathematics", "89"};
        assertThat(function.apply(line), is("みけ"));
    }


Function<T, R>Tintlongdoubleになった

  • IntFunction<R>
  • LongFunction<R>
  • DoubleFunction<R>

があり、それぞれ

  • IntStream#mapToObj
  • LongStream#mapToObj
  • DoubleStream#mapToObj

で利用されます。

    @Test
    public void intFunction_Rのサンプル () {
        IntFunction<Character> function = (value) -> (char)value;

        int ch = 65;
        assertThat(function.apply(ch), is('A'));
    }


Function<T, R>Rintlongdoubleとなっている

  • ToIntFunction<T>
  • ToLongFunction<T>
  • ToDoubleFunction<T>

があります。それぞれ

  • Stream#mapToInt
  • Stream#mapToLong
  • Stream#mapToDouble

で用いられます。

    @Test
    public void toLongFunction_Tのサンプル () {
        ToLongFunction<Date> function = (date) -> date.getTime();

        Calendar calendar = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        calendar.set(1970, Calendar.JANUARY, 1, 0, 0, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        Date date = calendar.getTime();

        assertThat(function.applyAsLong(date), is(0L));
    }


さらに、TRの組み合わせによって次のようなものもあります。

T R 名前 利用メソッド
int long IntToLongFunction IntStream#mapToLong
int double IntToDoubleFunction IntStream#mapToDouble
long double LongToDoubleFunction LongStream#mapToDouble
long int LongToIntFunction LongStream#mapToInt
double int DoubleToIntFunction DoubleStream#mapToInt
double long DoubleToLongFunction DoubleStream#mapToLong

プリミティブのUnaryOperator

最初のStreamの生成(source)のプリミティブなUnaryOperatorや、

先ほどのPrimitiveToPrimitiveFunctionのところで書かなかったものがあります。

それが型の変換を伴わないFunctionで、

それがプリミティブのUnaryOperatorになります。

具体的には

  • IntUnaryOperator
  • LongUnaryOperator
  • DoubleUnaryOperator

です。

これらは、それぞれ、int/long/doubleの引数を1つ受け取り、

int/long/doubleを返すFunctional Interfaceです。

    @Test
    public void doubleUnaryOperatorのサンプル () {
        DoubleUnaryOperator operator = (value) -> value * value;

        double value = 0.5d;
        assertThat(operator.applyAsDouble(value), is(0.5d * 0.5d));
    }

Predicate<T>

T型の引数を1つ受け取り、booleanを返すFunctional Interfaceです。

条件を絞るなどの用途のStream#filterで用います。

    @Test
    public void predicate_Tのサンプル () {
        Predicate<Date> predicate = (date) -> date.getTime() < System.currentTimeMillis();

        Date date = new Date(System.currentTimeMillis() - 1000);
        assertThat(predicate.test(date), is(true));
    }

もちろん、intlongdouble用の

  • IntPredicate
  • LongPredicate
  • DoublePredicate

も用意されています。

    @Test
    public void intPredicate_Tのサンプル () {
        IntPredicate predicate = (value) -> value % 2 == 0;

        int value = 11;
        assertThat(predicate.test(value), is(false));
    }

Comparator<T>

これは以前からあったものですが、

一応、ここにも上げておきたいと思います。

T型の引数を2個受け取り、intの値を返すFunctional Interfaceです。

Stream#sortedで利用します。

なお、IntStreamLongStreamDoubleStreamでは、

Comparator<Integer>Comparator<Long>Comparator<Double>

引数として受け取るsortedメソッドはありません。

ほんと、どうなっているんですかね(´・ω・`)

    @Test
    public void comparator_Tのサンプル () {
        Comparator<String> comparator = (left, right) -> left.compareTo(right);

        String left = "January";
        String right = "December";

        assertThat(comparator.compare(left, right), is(graterThan(0)));
    }

終端処理(terminal operation)に用いるもの

Consumer<T>

T型の引数を1つ受け取り、何も返さないFunctional Interfaceです。

Stream#forEachStream#forEachOrderedで利用します。

    @Test
    public void consumer_Tのサンプル () {
        final StringBuilder builder = new StringBuilder("Hello ");
        Consumer<String> consumer = builder::append;

        consumer.accept("いちろー");
        assertThat(builder.toString(), is("Hello いちろー"));
    }

なお、これにも、intlongdouble用の

  • IntConsumer
  • LongConsumer
  • DoubleConsumer

があります。

    @Test
    public void intConsumerのサンプル () {
        final AtomicInteger value = new AtomicInteger(10);
        IntConsumer consumer = value::addAndGet;

        consumer.accept(1);
        assertThat(value.get(), is(11));
    }

method referenceについて

Functional Interfaceを書く場合に特殊な書き方になる場合があります。それがmethod referenceとよばれている書き方です。

引数を1つ受け取り、その引数を別のオブジェクトや別のクラスのstaticメソッドの引数とする場合などに、用いられる書き方です。

例えば、引数をそのままSystem.out.printlnに渡すConsumerは、次のように書きます。

System.out::println

またZoneIdの文字列からZoneIdに変換するFunction<String, ZoneId>は次のように書きます。

ZoneId::of

さらに、後述するBiConsumerなどで2つの引数の型がわかっている場合なども、この記法を用います。

BiConsumer<List<String>, String> consumer = List::add

これらの書き方はIDEが自動で変換してくれるので、素で書ける必要はないですが、コードを読むときに戸惑わないために、覚えておいたほうがよいと思います。


BinaryOperator<T>

T型の引数を2つ受け取り、T型のオブジェクトを返すFunctional Interfaceです。

  • Stream#reduce(BinaryOperator<T>)
  • Stream#reduce(T, BinaryOperator<T>)
  • Stream#reduce(U, BiFunction<U, T, U>, BinaryOperator<U>)

で利用します。

    @Test
    public void binaryOperator_Tのサンプル () {
        BinaryOperator<String> operator = (left, right) -> left + ' ' + right;

        assertThat(operator.apply("Hello", "World"), is("Hello World"));
    }

もちろん、intlongdouble用の

  • IntBinaryOperator
  • LongBinaryOperator
  • DoubleBinaryOperator

もあります。

    @Test
    public void doubleBinaryOperatorのサンプル () {
        DoubleBinaryOperator operator = (left, right) -> left * right;

        assertThat(operator.applyAsDouble(0.3d, 1.5d), is(0.3d * 1.5d));
    }

BiFunction<T, U, R>

T型の引数とU型の引数を受け取り、R型のオブジェクトを返すFunctional Interfaceです。

Stream#reduce(U, BiFunction<U, T, U>, BinaryOperator<U>)で利用します。

    @Test
    public void biFunction_T_U_Rのサンプル () {
        BiFunction<ZoneId, DateTimeFormatter, String> function = (zone, formatter) -> {
            LocalDate now = LocalDate.now(zone);
            return formatter.format(now);
        };

        ZoneId zone = ZoneId.of("Asia/Tokyo");
        DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy/MM");
        String now = new SimpleDateFormat("yyyy/MM").format(new Date());

        assertThat(function.apply(zone, pattern), is(now));
    }

BiConsumer<R, T>

R型の引数とT型の引数をとり、何も返さないFunctional Interfaceです。

Stream#collect(Supplier<R>, BiConsumer<R, T>, BiConsumer<R, R>)で利用します。

なお、Stream#collectで用いるときなどは、第一引数(R)の方に、

集合をまとめていくような形で書いていきます。

    @Test
    public void biConsumer_R_Tのサンプル () {
        BiConsumer<List<String>, String> consumer = List::add;

        List<String> list = new ArrayList<>(Arrays.asList("uno", "dos"));
        String three = "tres";

        consumer.accept(list, three);

        assertThat(list.size(), is(3));
        assertThat(list, hasItems("tres"));
    }

上記でTintlongdouble

  • ObjIntConsumer<R>
  • ObjLongConsumer<R>
  • ObjDoubleConsumer<R>

があります。

それぞれ、

  • IntStream#collect(Supplier<R>, ObjIntConsumer<R>, BiConsumer<R, R>)
  • LongStream#collect(Supplier<R>, ObjLongConsumer<R>, BiConsumer<R, R>)
  • DoubleStream#collect(Supplier<R>, ObjDoubleConsumer<R>, BiConsumer<R, R>)

で利用します。

    @Test
    public void objIntConsumer_Rのサンプル () {
        ObjIntConsumer<AtomicInteger> consumer = AtomicInteger::addAndGet;

        AtomicInteger sum = new AtomicInteger(10);
        consumer.accept(sum, 1);

        assertThat(sum.get(), is(11));
    }


長かったですねー

これでstream pipelineを書くために必要なFunctional Interfaceをひと通り紹介しました。

で、あまりに長すぎるので、チートシートを書いてみました。

なお、IntFunctionとかのプリミティブのやつは書いていません。


チートシート

Functional Interface 引数->戻り値
Supplier<T> () -> T
UnaryOperator<T> T -> T
Function<T, R> T -> R
Predicate<T> T -> boolean
Comparator<T> (T, T) -> int
Consumer<T> T -> Void
BinaryOperator<T> (T, T) -> T
BiFunction<T, U, R> (T, U) -> R
BiConsumer<R, T> (R, T) -> Void






おわり