Java 8 Collectors.toMap SortedMap

JavaLambdaJava 8Java StreamCollectors

Java 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 toMapimplementation 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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionRobert BainView Question on Stackoverflow
Solution 1 - JavadkatzelView Answer on Stackoverflow
Solution 2 - JavaRobert BainView Answer on Stackoverflow
Solution 3 - JavaTagir ValeevView Answer on Stackoverflow
Solution 4 - JavaDanielView Answer on Stackoverflow
Solution 5 - JavauweView Answer on Stackoverflow