こんにちわ、みけです。
もう、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
になっている
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')); }
なお、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#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>
のR
がint
、long
、double
となっている
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)); }
さらに、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
になります。
具体的には
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)); }
もちろん、int
、long
、double
用の
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
で利用します。
なお、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
用の
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")); }
もちろん、int
、long
、double
用の
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")); }
上記で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 |
おわり