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')); }
Tがint、long、doubleになっている
IntSupplierLongSupplierDoubleSupplier
というのもあり、
それぞれ、
IntStream#generateLongStream#generateDoubleStream#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')); }
なお、int、long、double用の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>のTがint、long、doubleになった
IntFunction<R>LongFunction<R>DoubleFunction<R>
があり、それぞれ
IntStream#mapToObjLongStream#mapToObjDoubleStream#mapToObj
で利用されます。
例
@Test public void intFunction_Rのサンプル () { IntFunction<Character> function = (value) -> (char)value; int ch = 65; assertThat(function.apply(ch), is('A')); }
Function<T, R>のRがint、long、doubleとなっている
ToIntFunction<T>ToLongFunction<T>ToDoubleFunction<T>
があります。それぞれ
Stream#mapToIntStream#mapToLongStream#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)); }
さらに、TとRの組み合わせによって次のようなものもあります。
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になります。
具体的には
IntUnaryOperatorLongUnaryOperatorDoubleUnaryOperator
です。
これらは、それぞれ、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)); }
もちろん、int、long、double用の
IntPredicateLongPredicateDoublePredicate
も用意されています。
例
@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で利用します。
なお、IntStreamやLongStreamやDoubleStreamでは、
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#forEach、Stream#forEachOrderedで利用します。
例
@Test public void consumer_Tのサンプル () { final StringBuilder builder = new StringBuilder("Hello "); Consumer<String> consumer = builder::append; consumer.accept("いちろー"); assertThat(builder.toString(), is("Hello いちろー")); }
なお、これにも、int、long、double用の
IntConsumerLongConsumerDoubleConsumer
があります。
例
@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")); }
もちろん、int、long、double用の
IntBinaryOperatorLongBinaryOperatorDoubleBinaryOperator
もあります。
例
@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")); }
上記でTがint、long、doubleの
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 |
おわり