# HG changeset patch
# User briangoetz
# Date 1376323610 14400
# Node ID a9b6d84af405547c7dc9cb3ff0cae56b5d9cc8e4
# Parent cc64a05836a7e6bd7eb275743b7d75c77850d905
8019401: Collectors.collectingAndThen
Reviewed-by:
Contributed-by: brian.goetz@oracle.com
diff --git a/src/share/classes/java/util/stream/Collectors.java b/src/share/classes/java/util/stream/Collectors.java
--- a/src/share/classes/java/util/stream/Collectors.java
+++ b/src/share/classes/java/util/stream/Collectors.java
@@ -354,6 +354,43 @@
}
/**
+ * 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
* result is 0.
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
--- 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 @@
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 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 @@
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) { }
+ }
+
}