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
?
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. )
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);
}
}
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()
.
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.
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.
来源:https://stackoverflow.com/questions/31004899/java-8-collectors-tomap-sortedmap