Java 8 Collectors.toMap SortedMap
JavaLambdaJava 8Java StreamCollectorsJava Problem Overview
I'm using Java 8 lambdas and want to use Collectors
toMap
to return a SortedMap
. The best I can come up with is to call the following Collectors
toMap
method with a dummy mergeFunction
and mapSupplier
equal to TreeMap::new
.
public static <T, K, U, M extends Map<K, U>>
Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction,
Supplier<M> mapSupplier) {
BiConsumer<M, T> accumulator = (map, element) -> map.merge(keyMapper.apply(element),
valueMapper.apply(element), mergeFunction);
return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
}
I don't want to pass in a merge function though, as I just want throwingMerger()
, in the same way as the basic toMap
implementation as follows:
public static <T, K, U>
Collector<T, ?, Map<K, U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
What would be the best practise method of using Collectors
to return a SortedMap
?
Java Solutions
Solution 1 - Java
I don't think you can get much better than this:
.collect(Collectors.toMap(keyMapper, valueMapper,
(v1,v2) ->{ throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2));},
TreeMap::new));
where the throw
lambda is the same as throwingMerger()
but I can't directly call that since it's package private (you can of course always make your own static method for that like throwingMerger()
is. )
Solution 2 - Java
Based on dkatzel's confirmation that there's not a nice API method, I've opted for maintaining my own custom Collectors class:
public final class StackOverflowExampleCollectors {
private StackOverflowExampleCollectors() {
throw new UnsupportedOperationException();
}
private static <T> BinaryOperator<T> throwingMerger() {
return (u, v) -> {
throw new IllegalStateException(String.format("Duplicate key %s", u));
};
}
public static <T, K, U, M extends Map<K, U>> Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper, Supplier<M> mapSupplier) {
return Collectors.toMap(keyMapper, valueMapper, throwingMerger(), mapSupplier);
}
}
Solution 3 - Java
Seems that there's no standard way to do this without defining your own throwingMerger()
method or using explicit lambda. In my StreamEx library I defined the toSortedMap
method which also uses my own throwingMerger()
.
Solution 4 - Java
Another way you can do this is to allow Collectors.toMap() to return whatever map it is going to return, and then pass that to a new TreeMap<>().
The caveat there is that this only works if your "hashCode()+equals()" and "compareTo" are consistent. If they aren't consistent, then you'll end up with the HashMap removing different set of keys than your TreeMap.
Solution 5 - Java
If you use the guava library then you can use:
.collect(ImmutableSortedMap.toImmutableSortedMap(comparator, keyMapper, valueMapper));
The resulting map will be a SortedMap
and also immutable.