Terminal operation either returns one value (of the same or another type than the input type) or does not return anything at all (produces only side effects). It does not allow another operation to be applied afterward and closes the stream.
In this post, we will continue covering the last of the terminal operations called collect():
R collect(Collector<T,A,R> collector)
It is a specialization of the reduce() operation. It allows implementing a vast variety of algorithms using the ready-to-use collectors from the java.util.stream.Collectors class. We discussed how to create a custom collector in Java streams 25. Collect 1. Custom collector. In this article, we will use only the collectors produced by the Collectors class.
Unmodifiable List, Set, Map
The List, Set, or Map is called unmodifiable in the sense that elements cannot be added, removed, or replaced. Calling any mutator method on an unmodifiable List, Set or Map triggers UnsupportedOperationException. However, if the contained elements are themselves mutable, this may cause the List‘s, Set‘s, or Map‘s contents to appear to change.
The unmodifiable List, Set or Map disallows null elements (disallows null key or value, in the case of Map, too). Attempts to create them with null results in NullPointerException.
The Collectors.toUnmodifiableList(), Collectors.toUnmodifiableSet(), and Collectors.toUnmodifiableMap() were introduced in Java 10.
Collectors.toUnmodifiableList()
The Collectors.toUnmodifiableList() collects stream elements in an unmodifiable List:
— Collector<T,?,List<T>> toUnmodifiableList() – returns a Collector that accumulates the input elements into an unmodifiable List in encounter order.
The following code demonstrates the collector’s behavior:
List<String> list = Stream.of("a", "a", "b")
.collect(Collectors.toUnmodifiableList());
System.out.println(list); //prints: [a, a, b]
System.out.println(list.getClass().getName());
//prints: java.util.ImmutableCollections$ListN
try {
list.add("c"); //prints: UnsupportedOperationException
} catch (Exception ex){
ex.printStackTrace();
}
Collectors.toUnmodifiableSet()
The Collectors.toUnmodifiableSet() collects stream elements in an unmodifiable Set:
— Collector<T,?,Set<T>> toUnmodifiableSet() – returns a Collector that accumulates the input elements into an unmodifiable Set.
The following code demonstrates the collector’s behavior:
Set<String> set = Stream.of("a", "a", "b")
.collect(Collectors.toUnmodifiableSet());
System.out.println(set); //prints: [a, b]
System.out.println(set.getClass().getName());
//prints: java.util.ImmutableCollections$Set12
try {
set.add("c"); //prints: UnsupportedOperationException
} catch (Exception ex){
ex.printStackTrace();
}
Collectors.toUnmodifiableMap()
The Collectors.toUnmodifiableMap() collects stream elements in an unmodifiable Map:
— Collector<T,?,Map<K,U>> toUnmodifiableMap(Function<T,K> keyMapper, Function<T,U> valueMapper) – returns a Collector that accumulates the input elements into an unmodifiable Map, whose keys and values are the result of applying the provided mapping functions to the input elements.
— Collector<T,?,Map<K,U>> toUnmodifiableMap(Function<? super T,K> keyMapper, Function<T,U> valueMapper, BinaryOperator<U> mergeFunction) – returns a Collector that accumulates the input elements into an unmodifiable Map, whose keys and values are the result of applying the provided mapping functions to the input elements; the mergeFunction resolves the case of the two equal key values.
The following code demonstrates the collector’s behavior. Let us create a Map that maps each stream element to its length:
try {
Map<String, Integer> map =
Stream.of("cat", "cat", "fish")
.collect(Collectors.
toUnmodifiableMap(Function.identity(),
String::length));
} catch (Exception ex){
ex.printStackTrace();
//prints: IllegalStateException: Duplicate key cat
// (attempted merging values 3 and 3)
}
As you can see, this version of toUnmodifiableMap() does not allow duplicate keys. We need to remove them either from the source or from the processing pipeline:
Map<String, Integer> map= Stream.of("cat", "cat", "fish")
.distinct() //That's how we remove duplicates
.collect(Collectors
.toUnmodifiableMap(Function.identity(),
String::length));
System.out.println(map); //prints: {cat=3, fish=4}
System.out.println(map4.getClass().getName());
//prints: java.util.ImmutableCollections$MapN
try {
map.put("c", 1); //prints: UnsupportedOperationException
} catch (Exception ex){
ex.printStackTrace();
}
Alternatively, we can use the second overloaded version of toUnmodifiableMap() and add the merge function that resolves the case of two equal keys:
Map<String, Integer> map = Stream.of("cat", "cat", "fish")
.collect(Collectors
.toUnmodifiableMap(Function.identity(),
String::length,
(i1,i2) -> i1 == i2 ? i1 : i2 ));
System.out.println(map); //prints: {cat=3, fish=4}
System.out.println(map4.getClass().getName());
//prints: java.util.ImmutableCollections$MapN
try {
map.put("c",1); //prints: UnsupportedOperationException
} catch (Exception ex){
ex.printStackTrace();
}
In the next post, we will discuss the functionality and usage of Collectors.collectingAndThen collector that allows adding finishing transformation.
See other posts on Java 8 streams and posts on other topics.
You can also use navigation pages for Java stream related blogs:
— Java 8 streams blog titles
— Create stream
— Stream operations
— Stream operation collect()
The source code of all the code examples is here in GitHub.
Send your comments using the link Contact or in response to my newsletter.
If you do not receive the newsletter, subscribe via link Subscribe under Contact.