From a1c24a7241fc7c68c6810cc04db2c161f0ad2bbf Mon Sep 17 00:00:00 2001 From: briangoetz Date: Mon, 12 Aug 2013 12:06:50 -0400 Subject: [PATCH] 8019401: Collectors.collectingAndThen Reviewed-by: mduigou Contributed-by: brian.goetz@oracle.com --- .../classes/java/util/stream/Collectors.java | 37 +++++++++++++++++++ .../java/util/stream/TabulatorsTest.java | 15 ++++++++ 2 files changed, 52 insertions(+) diff --git a/src/share/classes/java/util/stream/Collectors.java b/src/share/classes/java/util/stream/Collectors.java index 29a5464ee..27c7c83ea 100644 --- a/src/share/classes/java/util/stream/Collectors.java +++ b/src/share/classes/java/util/stream/Collectors.java @@ -353,6 +353,43 @@ public final class Collectors { downstream.characteristics()); } + /** + * Adapts a {@code Collector} to perform an additional finishing + * transformation. For example, one could adapt the {@link #toList()} + * collector to always produce an immutable list with: + *
{@code
+     *     List people
+     *         = people.stream().collect(collectingAndThen(toList(), Collections::unmodifiableList));
+     * }
+ * + * @param the type of the input elements + * @param intermediate accumulation type of the downstream collector + * @param result type of the downstream collector + * @param result type of the resulting collector + * @param downstream a collector + * @param finisher a function to be applied to the final result of the downstream collector + * @return a collector which performs the action of the downstream collector, + * followed by an additional finishing step + */ + public static Collector collectingAndThen(Collector downstream, + Function finisher) { + Set characteristics = downstream.characteristics(); + if (characteristics.contains(Collector.Characteristics.IDENTITY_FINISH)) { + if (characteristics.size() == 1) + characteristics = Collectors.CH_NOID; + else { + characteristics = EnumSet.copyOf(characteristics); + characteristics.remove(Collector.Characteristics.IDENTITY_FINISH); + characteristics = Collections.unmodifiableSet(characteristics); + } + } + return new CollectorImpl<>(downstream.supplier(), + downstream.accumulator(), + downstream.combiner(), + downstream.finisher().andThen(finisher), + characteristics); + } + /** * Returns a {@code Collector} accepting elements of type {@code T} that * counts the number of input elements. If no elements are present, the diff --git a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/TabulatorsTest.java b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/TabulatorsTest.java index 7505cb13f..f539df5d8 100644 --- a/test/java/util/stream/test/org/openjdk/tests/java/util/stream/TabulatorsTest.java +++ b/test/java/util/stream/test/org/openjdk/tests/java/util/stream/TabulatorsTest.java @@ -25,6 +25,7 @@ package org.openjdk.tests.java.util.stream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +53,7 @@ import java.util.stream.TestData; import org.testng.annotations.Test; +import static java.util.stream.Collectors.collectingAndThen; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.groupingByConcurrent; import static java.util.stream.Collectors.partitioningBy; @@ -603,4 +605,17 @@ public class TabulatorsTest extends OpTestCase { new PartitionAssertion<>(classifier, new ReduceAssertion<>(0, LambdaTestHelpers.identity(), Integer::sum))); } + + @Test(dataProvider = "StreamTestData", dataProviderClass = StreamTestDataProvider.class) + public void testComposeFinisher(String name, TestData.OfRef data) throws ReflectiveOperationException { + List asList = exerciseTerminalOps(data, s -> s.collect(toList())); + List asImmutableList = exerciseTerminalOps(data, s -> s.collect(collectingAndThen(toList(), Collections::unmodifiableList))); + assertEquals(asList, asImmutableList); + try { + asImmutableList.add(0); + fail("Expecting immutable result"); + } + catch (UnsupportedOperationException ignored) { } + } + } -- GitLab