Java8 Stream API 終端処理 (TerminalOperation)
java8 Stream APIの終端処理についてのメモです。
前回の続きで、Stream APIの終端処理について書きました。
pppurple.hatenablog.com
ストリーム生成⇒中間処理⇒終端処理の終端処理の部分です。
目次
- 終端処理
- count
- min
- max
- sum
- average
- summaryStatistics
- findFirst
- findAny
- anyMatch
- allMatch
- noneMatch
- reduce(identity, accumulator)
- reduce(accumulator)
- reduce(identity, accumulator, combiner)
- toArray
- iterator
- spliterator
- forEach
- forEachOrdered
- collect(supplier, accumulator, combiner)
- collect(collector)
- counting
- minBy
- maxBy
- summingInt
- averagingInt
- summarizingInt
- joining
- joining(delimiter)
- joining(delimiter, prefix, suffix)
- mapping
- reducing(identity, accumulator)
- reducing(accumulator)
- reducing(identity, accumulator, combiner)
- groupingBy(classifier)
- groupingBy(classifier, downstream)
- groupingBy(classifier, mapFactory, downstream)
- groupingByConcurrent(classifier)
- groupingByConcurrent(classifier, downstream)
- groupingByConcurrent(classifier, mapFactory, downstream)
- partitioningBy(predicate)
- partitioningBy(predicate, downstream)
- collectingAndThen
- toCollection
- toList
- toMap(keyMapper, valueMapper)
- toMap(keyMapper, valueMapper, mergeFunction)
- toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
- toConcurrentMap(keyMapper, valueMapper)
- toConcurrentMap(keyMapper, valueMapper, mergeFunction)
- toConcurrentMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
- toSet
- テストコード
終端処理
count
countはストリームの要素をカウントし、long型で返します。
@Test public void count() { long count = IntStream.rangeClosed(1, 10) .filter(i -> i > 5) .count(); assertThat(count).isEqualTo(5); }
min
minはストリームから最小の要素を返します。
IntStream、LongStream、DoubleStreamの場合、自然順序で最小値を返します。
戻り値はそれぞれ、OptionalInt、OptionalLong、OptionalDoubleになります。
Streamの場合は、引数で与えたComparatorに従って最小値を返します。
戻り値はOptionalになります。
@Test public void min() { // int OptionalInt min = IntStream.of(3, 5, 2, 8, 4) .min(); assertThat(min.getAsInt()).isEqualTo(2); // String Optional<String> minText = Stream.of("aaa", "bbb", "ccc") .min(Comparator.naturalOrder()); assertThat(minText.get()).isEqualTo("aaa"); }
max
maxはストリームから最大の要素を返します。
IntStream、LongStream、DoubleStreamの場合、自然順序で最大値を返します。
戻り値はそれぞれ、OptionalInt、OptionalLong、OptionalDoubleになります。
Streamの場合は、引数で与えたComparatorに従って最大値を返します。
戻り値はOptionalになります。
@Test public void max() { // int OptionalInt max = IntStream.of(3, 5, 2, 8, 4) .max(); assertThat(max.getAsInt()).isEqualTo(8); // String Optional<String> maxText = Stream.of("aaa", "bbb", "ccc") .max(Comparator.naturalOrder()); assertThat(maxText.get()).isEqualTo("ccc"); }
sum
sumはストリームの要素の合計を返します。
戻り値はIntStream、LongStream、DoubleStreamでそれぞれ、int、long、doubleになります。
@Test public void sum() { int sum = IntStream.rangeClosed(1, 10) .sum(); assertThat(sum).isEqualTo(55); }
average
averageはストリームの要素の平均を返します。
戻り値はOpotionalDoubleになります。
@Test public void average() { OptionalDouble avg = IntStream.rangeClosed(1, 10) .average(); assertThat(avg.getAsDouble()).isEqualTo(5.5); }
summaryStatistics
summaryStatisticsはIntSummaryStatisticsを返します。
IntSummaryStatisticsはカウント数、最小、最大、合計、平均などの統計情報を取得できます。
@Test public void summaryStatistics() { IntSummaryStatistics summary = IntStream.rangeClosed(1, 10) .summaryStatistics(); assertThat(summary.getMax()).isEqualTo(10); assertThat(summary.getMin()).isEqualTo(1); assertThat(summary.getAverage()).isEqualTo(5.5); assertThat(summary.getCount()).isEqualTo(10); assertThat(summary.getSum()).isEqualTo(55); }
findFirst
findFirstはストリームの最初の要素を返します。
戻り値はOptionalになります。
filterなどでフィルタリングされたストリームといっしょに使われます。
@Test public void findFirst() { Optional<String> first = Stream.of("a", "aa", "aaa") .filter(s -> s.length() > 1) .findFirst(); assertThat(first.get()).isEqualTo("aa"); }
findAny
findAnyはストリームの1つの要素を返します。
パフォーマンスのために、findFirstと違い最初の要素を返すわけではありません。
そのため同一の結果は保障されません。
static List<String> aList = Arrays.asList("aa", "aaa"); Condition<String> aCondtion = new Condition<>(aList::contains, "a match"); @Test public void findAny() { Optional<String> any = Stream.of("a", "aa", "aaa") .filter(s -> s.length() > 1) .findAny(); assertThat(any.get()).is(aCondtion); }
anyMatch
anyMatchはストリームのいずれかの要素が引数で指定した条件に一致するかを
booleanで返します。
@Test public void anyMatch() { boolean matchAny = IntStream.rangeClosed(1, 10) .anyMatch(i -> i > 8); assertThat(matchAny).isTrue(); boolean matchAny2 = IntStream.rangeClosed(1, 10) .anyMatch(i -> i > 10); assertThat(matchAny2).isFalse(); }
allMatch
allMatchはストリームのすべての要素が引数で指定した条件に一致するかを
booleanで返します。
@Test public void allMatch() { boolean matchAll = IntStream.rangeClosed(1, 10) .allMatch(i -> i > 0); assertThat(matchAll).isTrue(); boolean matchAll2 = IntStream.rangeClosed(1, 10) .allMatch(i -> i > 8); assertThat(matchAll2).isFalse(); }
noneMatch
noneMatchはストリームのいずれの要素も引数で指定した条件に一致しないかを
booleanで返します。
@Test public void noneMatch() { boolean matchNone = IntStream.rangeClosed(1, 10) .noneMatch(i -> i > 10); assertThat(matchNone).isTrue(); boolean matchNone2 = IntStream.rangeClosed(1, 10) .noneMatch(i -> i > 8); assertThat(matchNone2).isFalse(); }
reduce(identity, accumulator)
reduce(T identity, BinaryOperator<T> accumulator)はストリームの各要素に対して、accumulatorで指定した関数を適用します。
accumulatorはBinaryOperatorで第1引数に前回の処理結果、第2引数にストリームの要素となります。
一番最初の処理の場合、前回の処理結果がないため、単位元identityで指定した値を初期値として使用します。
@Test public void reduceWithIdentity() { // reduce(T identity, BinaryOperator<T> accumulator) int sum = IntStream.rangeClosed(1, 10) .reduce(1, (a, b) -> a + b); assertThat(sum).isEqualTo(56); String text = Stream.of("aaa", "bbb", "ccc") .reduce("#", (a, b) -> a + b); assertThat(text).isEqualTo("#aaabbbccc"); }
reduce(accumulator)
reduce(BinaryOperator<T> accumulator)は、reduce(T identity, BinaryOperator<T> accumulator)の
単位元indentityがないメソッドです。
そのためaccumulatorの処理は、ストリームの第1要素と第2要素の処理から始まります。
戻り値はOptionalになります。reduce(T identity, BinaryOperator<T> accumulator)の場合は単位元を指定しているので、
ストリームの要素がない場合もnullになることはありませんが、
reduce(BinaryOperator<T> accumulator)は単位元がないため、ストリームの要素がない場合、
nullとなる可能性があるためです。
@Test public void reduce() { // reduce(BinaryOperator<T> accumulator) OptionalInt sum = IntStream.rangeClosed(1, 10) .reduce((a, b) -> a + b); assertThat(sum.getAsInt()).isEqualTo(55); Optional<String> text = Stream.of("aaa", "bbb", "ccc") .reduce((a, b) -> a + b); assertThat(text.get()).isEqualTo("aaabbbccc"); }
reduce(identity, accumulator, combiner)
reduce(T identity, BinaryOperator<T> accumulator)は関数がBinaryOperatorになっているので、
ストリームの要素と戻り値は同じ型になります。
ストリームの要素と結果を異なる型にしたい場合、
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)を使用します。
単位元identityの指定は同じで、処理accumulatorはBiFunctionになっているので、何らかの型を返す関数を記述します。
第3引数のcombinerですが、これはパラレルストリームの際に処理結果を集約するために使用します。
そのためBinaryOperatorになっており、これはaccumulatorの戻り値と同じ型になります。
StreamをMapに変換する場合。
@Test public void reduceWithCombiner() { // reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) Map<Integer, String> map = Stream.of("a", "bb", "ccc", "dd") .reduce(new HashMap<>(), (m, s) -> { m.put(s.length(), s); return m; }, (m1, m2) -> { m1.putAll(m2); return m1; }); map.keySet().forEach(k -> System.out.println(k + ":" + map.get(k))); assertThat(map).containsOnly(entry(1, "a"), entry(2, "dd"), entry(3, "ccc")); }
toArray
toArrayはストリームの要素を配列で返します。
@Test public void toArray() { int[] ints = IntStream.rangeClosed(1, 5) .toArray(); assertThat(ints).containsSequence(1, 2, 3, 4, 5); String[] texts = Stream.of("aaa", "bbb", "ccc") .toArray(String[]::new); assertThat(texts).containsSequence("aaa", "bbb", "ccc"); }
iterator
@Test public void iterator() { PrimitiveIterator.OfInt ite = IntStream.rangeClosed(1, 10) .iterator(); while (ite.hasNext()) { System.out.println(ite.next()); } }
spliterator
spliteratorはストリームの要素のスプリッテレータを返します。
@Test public void spliterator() { Spliterator.OfInt spliterator = IntStream.of(3, 5, 2, 8, 4, 10, 6, 2, 8) .spliterator(); assertThat(spliterator.hasCharacteristics(Spliterator.SORTED)).isFalse(); Spliterator.OfInt orderdSpliterator = IntStream.of(3, 5, 2, 8, 4, 10, 6, 2, 8) .sorted() .spliterator(); assertThat(orderdSpliterator.hasCharacteristics(Spliterator.SORTED)).isTrue(); IntStream intStream = StreamSupport.intStream(spliterator, false); List<Integer> list = intStream.distinct() .sorted() .boxed() .collect(Collectors.toList()); List<Integer> expected = Arrays.asList(2, 3, 4, 5, 6, 8, 10); assertThat(list).isEqualTo(expected); }
forEach
forEachはConsumerインターフェースを引数にとるため、結果を返さない処理を記述します。
@Test public void forEach() { IntStream.rangeClosed(1, 10) .forEach(System.out::println); }
結果
1 2 3 4 5 6 7 8 9 10
forEachOrdered
forEachは順序を保証しませんが、forEachOrderedでは順序付けされている場合、順序が保証されます。
@Test public void forEachOrdered() { // forEach IntStream.rangeClosed(1, 10) .parallel() .forEach(System.out::println); // forEachOrdered IntStream.rangeClosed(1, 10) .parallel() .forEachOrdered(System.out::println); }
結果
// forEach 7 6 2 9 3 1 4 8 5 10 // forEachOrdered 1 2 3 4 5 6 7 8 9 10
collect(supplier, accumulator, combiner)
collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)は
reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)とほとんど同じです。
supplierで戻り値となる型を生成します。
accumulatorでストリームの処理を記述します。第1引数でsupplier、第2引数でストリームの要素を取ります。
reduceと異なるのはBiConsumerとなっている点で、戻り値は不要で、supplierへの処理が引き継がれます。
combinerはパラレルストリームで使用される集約処理です。
これもreduceと異なり、BiConcumerになっており、戻り値は不要で、集約処理を記述します。
StreamをMapに変換する場合。
@Test public void collect() { Map<Integer, String> map = Stream.of("a", "bb", "ccc", "dd") .collect(HashMap::new, (m, s) -> m.put(s.length(), s), HashMap::putAll); assertThat(map).containsOnly(entry(1, "a"), entry(2, "dd"), entry(3, "ccc")); }
collect(collector)
collect(Collector<? super T,A,R> collector)は、java.util.stream.Collectorインターフェースを取ります。
Collectorインターフェースの実装として、java.util.stream.Collectorsクラスがあり、便利なメソッドがたくさん定義されています。
下記にjava.util.stream.Collectorsクラスのメソッドを記載します。
counting
Collectors.countingはストリームの要素数を返します。
戻り値はLong型です。
@Test public void counting() { Long count = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.counting()); assertThat(count).isEqualTo(10); Long countStr = Stream.of("a", "b", "c", "d") .collect(Collectors.counting()); assertThat(countStr).isEqualTo(4); }
minBy
Collectors.minByはストリームの最小要素を返します。
引数として比較のためのComparatorを指定します。
戻り値はOptional型となります。
@Test public void minBy() { Integer min = Stream.of(2, 3, 8, 5, 6) .collect(Collectors.minBy(Integer::compare)) .get(); assertThat(min).isEqualTo(2); }
maxBy
Collectors.maxByはストリームの最大要素を返します。
引数として比較のためのComparatorを指定します。
戻り値はOptional型となります。
@Test public void maxBy() { Integer max = Stream.of(2, 3, 8, 5, 6) .collect(Collectors.maxBy(Integer::compare)) .get(); assertThat(max).isEqualTo(8); }
summingInt
Collectors.summingIntは関数の処理結果のint値の合計を返します。
ToIntFunctionを引数に取るので、戻り値がintとなる関数を記述します。
戻り値はInteger型となります。
同様にlong値を合計するsummingLong
double値を合計するsummingDoubleがあります。
@Test public void summingInt() { int sum = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.summingInt(Integer::intValue)); assertThat(sum).isEqualTo(55); int lengthSum = Stream.of("a", "bb", "ccc") .collect(Collectors.summingInt(String::length)); assertThat(lengthSum).isEqualTo(6); }
averagingInt
Collectors.averagingIntは関数の処理結果のint値の平均を返します。
ToIntFunctionを引数に取るので、戻り値がintとなる関数を記述します。
戻り値はDouble型となります。
同様に
・long値を平均するaveragingLong
・double値を平均するaveragingDouble
があります。
戻り値はすべてDouble型となります。
@Test public void averagingInt() { double avg = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.averagingInt(Integer::intValue)); assertThat(avg).isEqualTo(5.5); double lengthAvg = Stream.of("a", "bb", "ccc") .collect(Collectors.averagingInt(String::length)); assertThat(lengthAvg).isEqualTo(2.0); }
summarizingInt
Collectors.summarizingIntは関数の処理結果のint値のサマリー統計を返します。
ToIntFunctionを引数に取るので、戻り値がintとなる関数を記述します。
戻り値はIntSummaryStatistics型となります。
IntSummaryStatisticsはカウント数、最小、最大、合計、平均などの統計情報を持つクラスです。
下記のメソッドを持っていて、統計情報を取得できます。
- getAverage():平均を返す
- getCount():値をカウントして返す
- getMax():最大値を返す
- getMin():最小値を返す
- getSum():合計を返す
同様に
・LongSummaryStatisticsを返すsummarizingLong、
・DoubleSummaryStatisticsを返すsummarizingDouble
があります。
@Test public void summarizingInt() { IntSummaryStatistics stat = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.summarizingInt(Integer::intValue)); int max = stat.getMax(); int min = stat.getMin(); double sum = stat.getSum(); double avg = stat.getAverage(); long count = stat.getCount(); assertThat(max).isEqualTo(10); assertThat(min).isEqualTo(1); assertThat(sum).isEqualTo(55); assertThat(avg).isEqualTo(5.5); assertThat(count).isEqualTo(10); }
joining
Collectors.joiningはストリームの要素を連結した文字列を返します。
@Test public void joining() { // String String text = Stream.of("aaa", "bbb", "ccc") .map(String::toUpperCase) .collect(Collectors.joining()); assertThat(text).isEqualTo("AAABBBCCC"); // int String ints = IntStream.rangeClosed(1, 9) .mapToObj(String::valueOf) .collect(Collectors.joining()); assertThat(ints).isEqualTo("123456789"); }
joining(delimiter)
Collectors.joining(CharSequence delimiter)はストリームの要素をdelimiterを区切り文字として連結した文字列を返します。
@Test public void joiningWithDelimiter() { String csv = IntStream.rangeClosed(1, 9) .mapToObj(String::valueOf) .collect(Collectors.joining(",")); assertThat(csv).isEqualTo("1,2,3,4,5,6,7,8,9"); }
joining(delimiter, prefix, suffix)
Collectors.joining(CharSequence delimiter, CharSequence prefix, CharSequence suffix)は、
ストリームの要素をdelimiterを区切り文字として連結し、前後にprefixとsuffixを連結した文字列を返します。
@Test public void joiningWithPrefixAndSuffix() { String csv = IntStream.rangeClosed(1, 9) .mapToObj(String::valueOf) .collect(Collectors.joining(",", "[", "]")); assertThat(csv).isEqualTo("[1,2,3,4,5,6,7,8,9]"); }
mapping
Collectors.mappingはストリームの要素を第1引数で処理し、結果を第2引数のCollectorで集約します。
第1引数はFunctionインターフェースなので、map()のようにマッピング処理を行います。
結果は第2引数でCollectorインターフェースでの処理(Collectorsクラスの処理)を記述します。
@Test public void mapping() { // Collectors.mapping List<String> upperTextMapping = Stream.of("aaa", "bbb", "ccc") .collect(Collectors.mapping(String::toUpperCase, Collectors.toList())); assertThat(upperTextMapping).containsSequence("AAA", "BBB", "CCC"); // map() List<String> upperText = Stream.of("aaa", "bbb", "ccc") .map(String::toUpperCase) .collect(Collectors.toList()); assertThat(upperText).containsSequence("AAA", "BBB", "CCC"); }
reducing(identity, accumulator)
Collectors.reducing(T identity, BinaryOperator<T> accumulator)はストリームの各要素に対して、
accumulatorで指定した関数を適用します。
accumulatorはBinaryOperatorで第1引数に前回の処理結果、第2引数にストリームの要素となります。
一番最初の処理の場合、前回の処理結果がないため、単位元identityで指定した値を初期値として使用します。
@Test public void reducingWithIdentity() { // Collectors.reducing(T identity, BinaryOperator<T> op) int sumReducing = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.reducing(1, (sum, i) -> sum + i)); assertThat(sumReducing).isEqualTo(56); String text = Stream.of("aaa", "bbb", "ccc") .collect(Collectors.reducing("#", (a, b) -> a + b)); assertThat(text).isEqualTo("#aaabbbccc"); }
reducing(accumulator)
Collectors.reducing(BinaryOperator<T> accumulator)は、Collectors.reduce(T identity, BinaryOperator<T> accumulator)の
単位元indentityがないメソッドです。
そのためaccumulatorの処理は、ストリームの第1要素と第2要素の処理から始まります。
戻り値はOptionalになります。Collectors.reduce(T identity, BinaryOperator<T> accumulator)の場合は単位元を指定しているので、
ストリームの要素がない場合もnullになることはありませんが、
Collectors.reduce(BinaryOperator<T> accumulator)は単位元がないため、ストリームの要素がない場合、
nullとなる可能性があるためです。
@Test public void reducing() { // Collectors.reducing(BinaryOperator<T> op) Optional<String> text = Stream.of("aaa", "bbb", "ccc") .collect(Collectors.reducing((a, b) -> a + b)); assertThat(text.get()).isEqualTo("aaabbbccc"); }
reducing(identity, accumulator, combiner)
Collectors.reducing(T identity, BinaryOperator<T> accumulator)は
関数がBinaryOperatorになっているので、ストリームの要素と戻り値は同じ型になります。
ストリームの要素と結果を異なる型にしたい場合、
Collectors.reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner)を使用します。
単位元identityの指定は同じで、処理accumulatorはBiFunctionになっているので、何らかの型を返す関数を記述します。
第3引数のcombinerですが、これはパラレルストリームの際に処理結果を集約するために使用します。
そのためBinaryOperatorになっており、これはaccumulatorの戻り値と同じ型になります。
@Test public void reducingWithMapper() { // Collectors.reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op) int sumLength = Stream.of("a", "bb", "ccc", "dd") .collect(Collectors.reducing(100, String::length, (a, b) -> a + b)); assertThat(sumLength).isEqualTo(108); }
groupingBy(classifier)
Collectors.groupingBy(Function<? super T,? extends K> classifier)は、classifierで指定した関数に従って
グルーピングした結果を返します。
@Test public void groupingBy() { // Collectors.groupingBy(Function<? super T,? extends K> classifier) Map<Integer, List<String>> lengthMap = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingBy(String::length)); assertThat(lengthMap).containsOnlyKeys(1, 2, 3); assertThat(lengthMap).contains(entry(1, Arrays.asList("a", "d"))); assertThat(lengthMap).contains(entry(2, Arrays.asList("bb", "ee"))); assertThat(lengthMap).contains(entry(3, Arrays.asList("ccc", "fff"))); assertThat(lengthMap).containsOnly(entry(1, Arrays.asList("a", "d")), entry(2, Arrays.asList("bb", "ee")), entry(3, Arrays.asList("ccc", "fff"))); }
groupingBy(classifier, downstream)
Collectors.groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)は、
classifierで指定した関数に従ってグルーピングした結果に対し、
downstreamで指定したCollectorインターフェースを実装した関数(Collectorsクラスのメソッド)を適用します。
@Test public void groupingByWithDownstream() { // Collectors.groupingBy(Function<? super T,? extends K> classifier, // Collector<? super T,A,D> downstream) Map<Integer, Long> countLength = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingBy(String::length, Collectors.counting())); assertThat(countLength).containsOnly(entry(1, 2L), entry(2, 2L), entry(3, 2L)); }
groupingBy(classifier, mapFactory, downstream)
Collectors.groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)は、
classifierで指定した関数に従ってグルーピングした結果に対し、
downstreamで指定したCollectorインターフェースを実装した関数(Collectorsクラスのメソッド)を適用した結果をMapに格納します。
@Test public void groupingByWithMapFactory() { // Collectors.groupingBy(Function<? super T,? extends K> classifier, // Supplier<M> mapFactory, // Collector<? super T,A,D> downstream) Map<String, Long> stringCount = Stream.of("a", "bb", "ccc", "A", "dd", "CCC") .collect(Collectors.groupingBy(String::toUpperCase, TreeMap::new, Collectors.counting())); assertThat(stringCount).containsOnlyKeys("A", "BB", "CCC", "DD"); assertThat(stringCount).containsValues(2L, 1L, 2L, 1L); }
groupingByConcurrent(classifier)
Collectors.groupingByConcurrent(Function<? super T,? extends K> classifier)は、
Collectors.groupingBy(Function<? super T,? extends K> classifier)と同じ引数を取りますが、ConcurrentMapを返します。
@Test public void groupingByConcurrent() { // groupingByConcurrent(Function<? super T,? extends K> classifier, // Collector<? super T,A,D> downstream) ConcurrentMap<Integer, List<String>> lengthMap = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingByConcurrent(String::length)); assertThat(lengthMap).containsOnlyKeys(1, 2, 3); assertThat(lengthMap).contains(entry(1, Arrays.asList("a", "d"))); assertThat(lengthMap).contains(entry(2, Arrays.asList("bb", "ee"))); assertThat(lengthMap).contains(entry(3, Arrays.asList("ccc", "fff"))); assertThat(lengthMap).containsOnly(entry(1, Arrays.asList("a", "d")), entry(2, Arrays.asList("bb", "ee")), entry(3, Arrays.asList("ccc", "fff"))); }
groupingByConcurrent(classifier, downstream)
Collectors.groupingByConcurrent(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)は、
Collectors.groupingBy(Function<? super T,? extends K> classifier, Collector<? super T,A,D> downstream)と同じ引数を取りますが、
ConcurrentMapを返します。
@Test public void groupingByConcurrentWithDownstream() { ConcurrentMap<Integer, Long> countLength = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingByConcurrent(String::length, Collectors.counting())); assertThat(countLength).containsOnly(entry(1, 2L), entry(2, 2L), entry(3, 2L)); }
groupingByConcurrent(classifier, mapFactory, downstream)
Collectors.groupingByConcurrent(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)は、
Collectors.groupingBy(Function<? super T,? extends K> classifier, Supplier<M> mapFactory, Collector<? super T,A,D> downstream)と同じ引数を取りますが、ConcurrentMapを返します。
@Test public void groupingByConcurrentWithMapFactory() { ConcurrentMap<String, Long> stringCount = Stream.of("a", "bb", "ccc", "A", "dd", "CCC") .collect(Collectors.groupingByConcurrent(String::toUpperCase, ConcurrentSkipListMap::new, Collectors.counting())); assertThat(stringCount).containsOnlyKeys("A", "BB", "CCC", "DD"); assertThat(stringCount).containsValues(2L, 1L, 2L, 1L); }
partitioningBy(predicate)
Collectors.partitioningBy(Predicate<? super T> predicate)は、predicateで指定した条件の結果をMapに格納します。
Predicateインターフェースはbooleanを返す関数を記述します。結果がtrueのものと、falseのものでそれぞれMapに格納されます。
@Test public void partitioningBy() { Map<Boolean, List<String>> length3 = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.partitioningBy(s -> s.length() > 2)); assertThat(length3).contains(entry(true, Arrays.asList("ccc", "fff"))); assertThat(length3).contains(entry(false, Arrays.asList("a", "bb", "d", "ee"))); }
partitioningBy(predicate, downstream)
Collectors.partitioningBy(Predicate<? super T> predicate, Collector<? super T,A,D> downstream)は、
predicateで指定した条件の結果に対して、downstreamで指定した関数を適用した結果をMapに格納します。
Predicateインターフェースはbooleanを返す関数を記述します。結果がtrueのものと、falseのものでそれぞれMapに格納されます。
downstreamはCollectorインターフェースを実装した関数(Collectorsクラスのメソッド)を記述します。
@Test public void partitioningByWithDownstream() { Map<Boolean, Long> length3 = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.partitioningBy(s -> s.length() > 2, Collectors.counting())); assertThat(length3).contains(entry(true, 2L)); assertThat(length3).contains(entry(false, 4L)); }
collectingAndThen
Collectors.collectingAndThenはcollect処理した結果に後処理を行います。
第1引数でcollect処理を行い、第2引数ではFunctionインターフェースを取る後処理を記述します。
@Test public void collectingAndThen() { List<String> list = Stream.of("a", "b", "c", "d", "e") .map(String::toUpperCase) .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); assertThat(list).containsOnly("A", "B", "C", "D", "E"); }
toCollection
Collectors.toCollectionはストリームの要素を含むコレクションを返します。
引数としてコレクションを生成する処理を記述します。
@Test public void toCollection() { List<Integer> list = IntStream.rangeClosed(1, 5) .boxed() .collect(Collectors.toCollection(LinkedList::new)); assertThat(list).containsOnly(1, 2, 3, 4, 5); }
toList
Collectors.toListはストリームの要素をListで返します。
@Test public void toList() { // int List<Integer> list = IntStream.rangeClosed(1, 5) .map(i -> i + 100) .boxed() .collect(Collectors.toList()); assertThat(list).containsSequence(101, 102, 103, 104, 105); // String List<String> text = Stream.of("aaa", "bbb", "ccc") .map(String::toUpperCase) .collect(Collectors.toList()); assertThat(text).containsSequence("AAA", "BBB", "CCC"); }
toMap(keyMapper, valueMapper)
Collectors.toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)は
ストリームの要素をMapで返します。
keyとvalueを得るためのFunctionインターフェースを実装した関数を記述します。
@Test public void toMap() { // Collectors.toMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper) Map<String, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toMap(s -> s, String::toUpperCase)); assertThat(upper).contains(entry("a", "A")); assertThat(upper).contains(entry("bb", "BB")); assertThat(upper).contains(entry("ccc", "CCC")); assertThat(upper).contains(entry("d", "D")); assertThat(upper).contains(entry("ee", "EE")); assertThat(upper).contains(entry("fff", "FFF")); }
toMap(keyMapper, valueMapper, mergeFunction)
Collectors.toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)は
ストリームの要素をMapで返します。
keyとvalueを得るためのFunctionインターフェースを実装した関数を記述します。
マップのキーが重複する場合は、指定したmergeFunctionでvalueがマージされます。
@Test public void toMapWithMerge() { // Collectors.toMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction) Map<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); }
toMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
Collectors.toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)は
ストリームの要素をMapで返します。
keyとvalueを得るためのFunctionインターフェースを実装した関数を記述します。
マップのキーが重複する場合は、指定したmergeFunctionでvalueがマージされます。
パラレルストリームで処理する場合、mapSupplierで指定した関数で統合されます。
@Test public void toMapWithMapSupplier() { // Collectors.toMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction, // Supplier<M> mapSupplier) Map<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2, HashMap::new)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); }
toConcurrentMap(keyMapper, valueMapper)
Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)は、
Collectors.toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper)と引数は同じですが、
ConcurrentMapを返します。
@Test public void toConcurrentMap() { // Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper) ConcurrentMap<String, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toConcurrentMap(s -> s, String::toUpperCase)); assertThat(upper).contains(entry("a", "A")); assertThat(upper).contains(entry("bb", "BB")); assertThat(upper).contains(entry("ccc", "CCC")); assertThat(upper).contains(entry("d", "D")); assertThat(upper).contains(entry("ee", "EE")); assertThat(upper).contains(entry("fff", "FFF")); }
toConcurrentMap(keyMapper, valueMapper, mergeFunction)
Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)は、
Collectors.toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction)と引数は同じですが、ConcurrentMapを返します。
@Test public void toConcurrentMapWithMerge() { // Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction) ConcurrentMap<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toConcurrentMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); }
toConcurrentMap(keyMapper, valueMapper, mergeFunction, mapSupplier)
Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)は
Collectors.toMap(Function<? super T,? extends K> keyMapper, Function<? super T,? extends U> valueMapper, BinaryOperator<U> mergeFunction, Supplier<M> mapSupplier)と引数は同じですが、ConcurrentMapを返します。
@Test public void toConcurrentMapWithMapSupplier() { // Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction, // Supplier<M> mapSupplier) ConcurrentMap<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toConcurrentMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2, ConcurrentHashMap::new)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); }
toSet
Collectors.toSetはストリームの要素からSetを生成します。
@Test public void toSet() { Set<String> set = Stream.of("a", "b", "c", "b", "d") .collect(Collectors.toSet()); assertThat(set).containsOnly("a", "b", "c", "d"); }
テストコード
今回のテストコードの全体です。
package javase8; import org.assertj.core.api.Condition; import org.junit.Test; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.IntSummaryStatistics; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.OptionalDouble; import java.util.OptionalInt; import java.util.PrimitiveIterator; import java.util.Set; import java.util.Spliterator; import java.util.TreeMap; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.stream.Collectors; import java.util.stream.IntStream; import java.util.stream.Stream; import java.util.stream.StreamSupport; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.entry; public class StreamApiTerminalOperationTest { @Test public void count() { long count = IntStream.rangeClosed(1, 10) .filter(i -> i > 5) .count(); assertThat(count).isEqualTo(5); } @Test public void min() { // int OptionalInt min = IntStream.of(3, 5, 2, 8, 4) .min(); assertThat(min.getAsInt()).isEqualTo(2); // String Optional<String> minText = Stream.of("aaa", "bbb", "ccc") .min(Comparator.naturalOrder()); assertThat(minText.get()).isEqualTo("aaa"); } @Test public void max() { // int OptionalInt max = IntStream.of(3, 5, 2, 8, 4) .max(); assertThat(max.getAsInt()).isEqualTo(8); // String Optional<String> maxText = Stream.of("aaa", "bbb", "ccc") .max(Comparator.naturalOrder()); assertThat(maxText.get()).isEqualTo("ccc"); } @Test public void sum() { int sum = IntStream.rangeClosed(1, 10) .sum(); assertThat(sum).isEqualTo(55); } @Test public void average() { OptionalDouble avg = IntStream.rangeClosed(1, 10) .average(); assertThat(avg.getAsDouble()).isEqualTo(5.5); } @Test public void summaryStatistics() { IntSummaryStatistics summary = IntStream.rangeClosed(1, 10) .summaryStatistics(); assertThat(summary.getMax()).isEqualTo(10); assertThat(summary.getMin()).isEqualTo(1); assertThat(summary.getAverage()).isEqualTo(5.5); assertThat(summary.getCount()).isEqualTo(10); assertThat(summary.getSum()).isEqualTo(55); } @Test public void findFirst() { Optional<String> first = Stream.of("a", "aa", "aaa") .filter(s -> s.length() > 1) .findFirst(); assertThat(first.get()).isEqualTo("aa"); } static List<String> aList = Arrays.asList("aa", "aaa"); Condition<String> aCondtion = new Condition<>(aList::contains, "a match"); @Test public void findAny() { Optional<String> any = Stream.of("a", "aa", "aaa") .filter(s -> s.length() > 1) .findAny(); assertThat(any.get()).is(aCondtion); } @Test public void anyMatch() { boolean matchAny = IntStream.rangeClosed(1, 10) .anyMatch(i -> i > 8); assertThat(matchAny).isTrue(); boolean matchAny2 = IntStream.rangeClosed(1, 10) .anyMatch(i -> i > 10); assertThat(matchAny2).isFalse(); } @Test public void allMatch() { boolean matchAll = IntStream.rangeClosed(1, 10) .allMatch(i -> i > 0); assertThat(matchAll).isTrue(); boolean matchAll2 = IntStream.rangeClosed(1, 10) .allMatch(i -> i > 8); assertThat(matchAll2).isFalse(); } @Test public void noneMatch() { boolean matchNone = IntStream.rangeClosed(1, 10) .noneMatch(i -> i > 10); assertThat(matchNone).isTrue(); boolean matchNone2 = IntStream.rangeClosed(1, 10) .noneMatch(i -> i > 8); assertThat(matchNone2).isFalse(); } @Test public void reduceWithIdentity() { // reduce(T identity, BinaryOperator<T> accumulator) int sum = IntStream.rangeClosed(1, 10) .reduce(1, (a, b) -> a + b); assertThat(sum).isEqualTo(56); String text = Stream.of("aaa", "bbb", "ccc") .reduce("#", (a, b) -> a + b); assertThat(text).isEqualTo("#aaabbbccc"); } @Test public void reduce() { // reduce(BinaryOperator<T> accumulator) OptionalInt sum = IntStream.rangeClosed(1, 10) .reduce((a, b) -> a + b); assertThat(sum.getAsInt()).isEqualTo(55); Optional<String> text = Stream.of("aaa", "bbb", "ccc") .reduce((a, b) -> a + b); assertThat(text.get()).isEqualTo("aaabbbccc"); } @Test public void reduceWithCombiner() { // reduce(U identity, BiFunction<U,? super T,U> accumulator, BinaryOperator<U> combiner) Map<Integer, String> map = Stream.of("a", "bb", "ccc", "dd") .reduce(new HashMap<>(), (m, s) -> { m.put(s.length(), s); return m; }, (m1, m2) -> { m1.putAll(m2); return m1; }); map.keySet().forEach(k -> System.out.println(k + ":" + map.get(k))); assertThat(map).containsOnly(entry(1, "a"), entry(2, "dd"), entry(3, "ccc")); } @Test public void toArray() { int[] ints = IntStream.rangeClosed(1, 5) .toArray(); assertThat(ints).containsSequence(1, 2, 3, 4, 5); String[] texts = Stream.of("aaa", "bbb", "ccc") .toArray(String[]::new); assertThat(texts).containsSequence("aaa", "bbb", "ccc"); } @Test public void iterator() { PrimitiveIterator.OfInt ite = IntStream.rangeClosed(1, 10) .iterator(); while (ite.hasNext()) { System.out.println(ite.next()); } } @Test public void spliterator() { Spliterator.OfInt spliterator = IntStream.of(3, 5, 2, 8, 4, 10, 6, 2, 8) .spliterator(); assertThat(spliterator.hasCharacteristics(Spliterator.SORTED)).isFalse(); Spliterator.OfInt orderdSpliterator = IntStream.of(3, 5, 2, 8, 4, 10, 6, 2, 8) .sorted() .spliterator(); assertThat(orderdSpliterator.hasCharacteristics(Spliterator.SORTED)).isTrue(); IntStream intStream = StreamSupport.intStream(spliterator, false); List<Integer> list = intStream.distinct() .sorted() .boxed() .collect(Collectors.toList()); List<Integer> expected = Arrays.asList(2, 3, 4, 5, 6, 8, 10); assertThat(list).isEqualTo(expected); } @Test public void forEach() { IntStream.rangeClosed(1, 10) .forEach(System.out::println); } @Test public void forEachOrdered() { // forEach IntStream.rangeClosed(1, 10) .parallel() .forEach(System.out::println); // forEachOrdered IntStream.rangeClosed(1, 10) .parallel() .forEachOrdered(System.out::println); } @Test public void collect() { Map<Integer, String> map = Stream.of("a", "bb", "ccc", "dd") .collect(HashMap::new, (m, s) -> m.put(s.length(), s), HashMap::putAll); assertThat(map).containsOnly(entry(1, "a"), entry(2, "dd"), entry(3, "ccc")); } @Test public void counting() { Long count = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.counting()); assertThat(count).isEqualTo(10); Long countStr = Stream.of("a", "b", "c", "d") .collect(Collectors.counting()); assertThat(countStr).isEqualTo(4); } @Test public void minBy() { Integer min = Stream.of(2, 3, 8, 5, 6) .collect(Collectors.minBy(Integer::compare)) .get(); assertThat(min).isEqualTo(2); } @Test public void maxBy() { Integer max = Stream.of(2, 3, 8, 5, 6) .collect(Collectors.maxBy(Integer::compare)) .get(); assertThat(max).isEqualTo(8); } @Test public void summingInt() { int sum = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.summingInt(Integer::intValue)); assertThat(sum).isEqualTo(55); int lengthSum = Stream.of("a", "bb", "ccc") .collect(Collectors.summingInt(String::length)); assertThat(lengthSum).isEqualTo(6); } @Test public void averagingInt() { double avg = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.averagingInt(Integer::intValue)); assertThat(avg).isEqualTo(5.5); double lengthAvg = Stream.of("a", "bb", "ccc") .collect(Collectors.averagingInt(String::length)); assertThat(lengthAvg).isEqualTo(2.0); } @Test public void summarizingInt() { IntSummaryStatistics stat = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.summarizingInt(Integer::intValue)); int max = stat.getMax(); int min = stat.getMin(); double sum = stat.getSum(); double avg = stat.getAverage(); long count = stat.getCount(); assertThat(max).isEqualTo(10); assertThat(min).isEqualTo(1); assertThat(sum).isEqualTo(55); assertThat(avg).isEqualTo(5.5); assertThat(count).isEqualTo(10); } @Test public void joining() { // String String text = Stream.of("aaa", "bbb", "ccc") .map(String::toUpperCase) .collect(Collectors.joining()); assertThat(text).isEqualTo("AAABBBCCC"); // int String ints = IntStream.rangeClosed(1, 9) .mapToObj(String::valueOf) .collect(Collectors.joining()); assertThat(ints).isEqualTo("123456789"); } @Test public void joiningWithDelimiter() { String csv = IntStream.rangeClosed(1, 9) .mapToObj(String::valueOf) .collect(Collectors.joining(",")); assertThat(csv).isEqualTo("1,2,3,4,5,6,7,8,9"); } @Test public void joiningWithPrefixAndSuffix() { String csv = IntStream.rangeClosed(1, 9) .mapToObj(String::valueOf) .collect(Collectors.joining(",", "[", "]")); assertThat(csv).isEqualTo("[1,2,3,4,5,6,7,8,9]"); } @Test public void mapping() { // Collectors.mapping List<String> upperTextMapping = Stream.of("aaa", "bbb", "ccc") .collect(Collectors.mapping(String::toUpperCase, Collectors.toList())); assertThat(upperTextMapping).containsSequence("AAA", "BBB", "CCC"); // map() List<String> upperText = Stream.of("aaa", "bbb", "ccc") .map(String::toUpperCase) .collect(Collectors.toList()); assertThat(upperText).containsSequence("AAA", "BBB", "CCC"); } @Test public void reducingWithIdentity() { // Collectors.reducing(T identity, BinaryOperator<T> op) int sumReducing = IntStream.rangeClosed(1, 10) .boxed() .collect(Collectors.reducing(1, (sum, i) -> sum + i)); assertThat(sumReducing).isEqualTo(56); String text = Stream.of("aaa", "bbb", "ccc") .collect(Collectors.reducing("#", (a, b) -> a + b)); assertThat(text).isEqualTo("#aaabbbccc"); } @Test public void reducing() { // Collectors.reducing(BinaryOperator<T> op) Optional<String> text = Stream.of("aaa", "bbb", "ccc") .collect(Collectors.reducing((a, b) -> a + b)); assertThat(text.get()).isEqualTo("aaabbbccc"); } @Test public void reducingWithMapper() { // Collectors.reducing(U identity, Function<? super T,? extends U> mapper, BinaryOperator<U> op) int sumLength = Stream.of("a", "bb", "ccc", "dd") .collect(Collectors.reducing(100, String::length, (a, b) -> a + b)); assertThat(sumLength).isEqualTo(108); } @Test public void groupingBy() { // Collectors.groupingBy(Function<? super T,? extends K> classifier) Map<Integer, List<String>> lengthMap = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingBy(String::length)); assertThat(lengthMap).containsOnlyKeys(1, 2, 3); assertThat(lengthMap).contains(entry(1, Arrays.asList("a", "d"))); assertThat(lengthMap).contains(entry(2, Arrays.asList("bb", "ee"))); assertThat(lengthMap).contains(entry(3, Arrays.asList("ccc", "fff"))); assertThat(lengthMap).containsOnly(entry(1, Arrays.asList("a", "d")), entry(2, Arrays.asList("bb", "ee")), entry(3, Arrays.asList("ccc", "fff"))); } @Test public void groupingByWithDownstream() { // Collectors.groupingBy(Function<? super T,? extends K> classifier, // Collector<? super T,A,D> downstream) Map<Integer, Long> countLength = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingBy(String::length, Collectors.counting())); assertThat(countLength).containsOnly(entry(1, 2L), entry(2, 2L), entry(3, 2L)); } @Test public void groupingByWithMapFactory() { // Collectors.groupingBy(Function<? super T,? extends K> classifier, // Supplier<M> mapFactory, // Collector<? super T,A,D> downstream) Map<String, Long> stringCount = Stream.of("a", "bb", "ccc", "A", "dd", "CCC") .collect(Collectors.groupingBy(String::toUpperCase, TreeMap::new, Collectors.counting())); assertThat(stringCount).containsOnlyKeys("A", "BB", "CCC", "DD"); assertThat(stringCount).containsValues(2L, 1L, 2L, 1L); } @Test public void groupingByConcurrent() { // groupingByConcurrent(Function<? super T,? extends K> classifier, // Collector<? super T,A,D> downstream) ConcurrentMap<Integer, List<String>> lengthMap = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingByConcurrent(String::length)); assertThat(lengthMap).containsOnlyKeys(1, 2, 3); assertThat(lengthMap).contains(entry(1, Arrays.asList("a", "d"))); assertThat(lengthMap).contains(entry(2, Arrays.asList("bb", "ee"))); assertThat(lengthMap).contains(entry(3, Arrays.asList("ccc", "fff"))); assertThat(lengthMap).containsOnly(entry(1, Arrays.asList("a", "d")), entry(2, Arrays.asList("bb", "ee")), entry(3, Arrays.asList("ccc", "fff"))); } @Test public void groupingByConcurrentWithDownstream() { ConcurrentMap<Integer, Long> countLength = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.groupingByConcurrent(String::length, Collectors.counting())); assertThat(countLength).containsOnly(entry(1, 2L), entry(2, 2L), entry(3, 2L)); } @Test public void groupingByConcurrentWithMapFactory() { ConcurrentMap<String, Long> stringCount = Stream.of("a", "bb", "ccc", "A", "dd", "CCC") .collect(Collectors.groupingByConcurrent(String::toUpperCase, ConcurrentSkipListMap::new, Collectors.counting())); assertThat(stringCount).containsOnlyKeys("A", "BB", "CCC", "DD"); assertThat(stringCount).containsValues(2L, 1L, 2L, 1L); } @Test public void partitioningBy() { Map<Boolean, List<String>> length3 = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.partitioningBy(s -> s.length() > 2)); assertThat(length3).contains(entry(true, Arrays.asList("ccc", "fff"))); assertThat(length3).contains(entry(false, Arrays.asList("a", "bb", "d", "ee"))); } @Test public void partitioningByWithDownstream() { Map<Boolean, Long> length3 = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.partitioningBy(s -> s.length() > 2, Collectors.counting())); assertThat(length3).contains(entry(true, 2L)); assertThat(length3).contains(entry(false, 4L)); } @Test public void collectingAndThen() { List<String> list = Stream.of("a", "b", "c", "d", "e") .map(String::toUpperCase) .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); assertThat(list).containsOnly("A", "B", "C", "D", "E"); } @Test public void toCollection() { List<Integer> list = IntStream.rangeClosed(1, 5) .boxed() .collect(Collectors.toCollection(LinkedList::new)); assertThat(list).containsOnly(1, 2, 3, 4, 5); } @Test public void toList() { // int List<Integer> list = IntStream.rangeClosed(1, 5) .map(i -> i + 100) .boxed() .collect(Collectors.toList()); assertThat(list).containsSequence(101, 102, 103, 104, 105); // String List<String> text = Stream.of("aaa", "bbb", "ccc") .map(String::toUpperCase) .collect(Collectors.toList()); assertThat(text).containsSequence("AAA", "BBB", "CCC"); } @Test public void toMap() { // Collectors.toMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper) Map<String, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toMap(s -> s, String::toUpperCase)); assertThat(upper).contains(entry("a", "A")); assertThat(upper).contains(entry("bb", "BB")); assertThat(upper).contains(entry("ccc", "CCC")); assertThat(upper).contains(entry("d", "D")); assertThat(upper).contains(entry("ee", "EE")); assertThat(upper).contains(entry("fff", "FFF")); } @Test public void toMapWithMerge() { // Collectors.toMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction) Map<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); } @Test public void toMapWithMapSupplier() { // Collectors.toMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction, // Supplier<M> mapSupplier) Map<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2, HashMap::new)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); } @Test public void toConcurrentMap() { // Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper) ConcurrentMap<String, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toConcurrentMap(s -> s, String::toUpperCase)); assertThat(upper).contains(entry("a", "A")); assertThat(upper).contains(entry("bb", "BB")); assertThat(upper).contains(entry("ccc", "CCC")); assertThat(upper).contains(entry("d", "D")); assertThat(upper).contains(entry("ee", "EE")); assertThat(upper).contains(entry("fff", "FFF")); } @Test public void toConcurrentMapWithMerge() { // Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction) ConcurrentMap<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toConcurrentMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); } @Test public void toConcurrentMapWithMapSupplier() { // Collectors.toConcurrentMap(Function<? super T,? extends K> keyMapper, // Function<? super T,? extends U> valueMapper, // BinaryOperator<U> mergeFunction, // Supplier<M> mapSupplier) ConcurrentMap<Integer, String> upper = Stream.of("a", "bb", "ccc", "d", "ee", "fff") .collect(Collectors.toConcurrentMap(String::length, s -> s, (s1, s2) -> s1 + "," + s2, ConcurrentHashMap::new)); assertThat(upper).contains(entry(1, "a,d")); assertThat(upper).contains(entry(2, "bb,ee")); assertThat(upper).contains(entry(3, "ccc,fff")); } @Test public void toSet() { Set<String> set = Stream.of("a", "b", "c", "b", "d") .collect(Collectors.toSet()); assertThat(set).containsOnly("a", "b", "c", "d"); } }
一応ソースはあげときました。
終わり。