--- old/src/java.base/share/classes/java/util/stream/Collectors.java 2017-09-20 16:53:11.000000000 -0700 +++ new/src/java.base/share/classes/java/util/stream/Collectors.java 2017-09-20 16:53:11.000000000 -0700 @@ -116,6 +116,8 @@ = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED, Collector.Characteristics.IDENTITY_FINISH)); static final Set CH_NOID = Collections.emptySet(); + static final Set CH_UNORDERED_NOID + = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.UNORDERED)); private Collectors() { } @@ -279,6 +281,24 @@ } /** + * Returns a {@code Collector} that accumulates the input elements into an + * unmodifiable {@code List}. + * + * @param the type of the input elements + * @return a {@code Collector} which collects all the input elements into a + * {@code List}, in encounter order + * @since 10 + */ + @SuppressWarnings("unchecked") + public static + Collector> toUnmodifiableList() { + return new CollectorImpl<>((Supplier>) ArrayList::new, List::add, + (left, right) -> { left.addAll(right); return left; }, + list -> (List)List.of(list.toArray()), + CH_NOID); + } + + /** * Returns a {@code Collector} that accumulates the input elements into a * new {@code Set}. There are no guarantees on the type, mutability, * serializability, or thread-safety of the {@code Set} returned; if more @@ -306,6 +326,33 @@ } /** + * Returns a {@code Collector} that accumulates the input elements into an + * unmodifiable {@code Set}. + * + *

This is an {@link Collector.Characteristics#UNORDERED unordered} + * Collector. + * + * @param the type of the input elements + * @return a {@code Collector} which collects all the input elements into a + * {@code Set} + * @since 10 + */ + @SuppressWarnings("unchecked") + public static + Collector> toUnmodifiableSet() { + return new CollectorImpl<>((Supplier>) HashSet::new, Set::add, + (left, right) -> { + if (left.size() < right.size()) { + right.addAll(left); return right; + } else { + left.addAll(right); return left; + } + }, + set -> (Set)Set.of(set.toArray()), + CH_UNORDERED_NOID); + } + + /** * Returns a {@code Collector} that concatenates the input elements into a * {@code String}, in encounter order. * @@ -1411,6 +1458,41 @@ } /** + * Returns a {@code Collector} that accumulates the input elements into an + * unmodifiable {@code Map}, + * whose keys and values are the result of applying the provided + * mapping functions to the input elements. + * + *

If the mapped keys contain duplicates (according to + * {@link Object#equals(Object)}), an {@code IllegalStateException} is + * thrown when the collection operation is performed. If the mapped keys + * may have duplicates, use {@link #toUnmodifiableMap(Function, Function, BinaryOperator)} + * instead. + * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @return a {@code Collector} which collects elements into an unmodifiable {@code Map} + * whose keys and values are the result of applying mapping functions to + * the input elements + * + * @see #toUnmodifiableMap(Function, Function, BinaryOperator) + * @since 10 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static + Collector> toUnmodifiableMap(Function keyMapper, + Function valueMapper) { + return new CollectorImpl<>(() -> new ArrayList>(), + (list, t) -> list.add(Map.entry(keyMapper.apply(t), valueMapper.apply(t))), + (list1, list2) -> { list1.addAll(list2); return list1; }, + list -> (Map)Map.ofEntries(list.toArray(new Map.Entry[0])), + CH_UNORDERED_NOID); + } + + /** * Returns a {@code Collector} that accumulates elements into a * {@code Map} whose keys and values are the result of applying the provided * mapping functions to the input elements. @@ -1473,6 +1555,45 @@ return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new); } + + /** + * Returns a {@code Collector} that accumulates the input elements into an + * unmodifiable {@code Map}, + * whose keys and values are the result of applying the provided + * mapping functions to the input elements. + * + *

If the mapped + * keys contain duplicates (according to {@link Object#equals(Object)}), + * the value mapping function is applied to each equal element, and the + * results are merged using the provided merging function. + * + * @param the type of the input elements + * @param the output type of the key mapping function + * @param the output type of the value mapping function + * @param keyMapper a mapping function to produce keys + * @param valueMapper a mapping function to produce values + * @param mergeFunction a merge function, used to resolve collisions between + * values associated with the same key, as supplied + * to {@link Map#merge(Object, Object, BiFunction)} + * @return a {@code Collector} which collects elements into an unmodifiable {@code Map} + * whose keys are the result of applying a key mapping function to the input + * elements, and whose values are the result of applying a value mapping + * function to all input elements equal to the key and combining them + * using the merge function + * + * @see #toUnmodifiableMap(Function, Function) + * @since 10 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + public static + Collector> toUnmodifiableMap(Function keyMapper, + Function valueMapper, + BinaryOperator mergeFunction) { + return collectingAndThen( + toMap(keyMapper, valueMapper, mergeFunction, HashMap::new), + hm -> (Map)Map.ofEntries(hm.entrySet().toArray(new Map.Entry[0]))); + } + /** * Returns a {@code Collector} that accumulates elements into a * {@code Map} whose keys and values are the result of applying the provided