Java 8 Collectors.toMap SortedMap


72

我正在使用Java 8 lambda,并且想要使用Collectors toMap返回一个SortedMap。我能想到的最好的Collectors toMap方法是使用一个虚拟对象mergeFunctionmapSupplier等于来调用以下方法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);
}

我不想像我只想要的那样传入合并函数,就像下面throwingMerger()的基本toMap实现一样:

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);
}

Collectors用来返回a的最佳实践方法是SortedMap什么?

Answers:


84

我认为您没有比这更好的了:

.collect(Collectors.toMap(keyMapper, valueMapper,
                        (v1,v2) ->{ throw new RuntimeException(String.format("Duplicate key for values %s and %s", v1, v2));},
                        TreeMap::new));

其中throwlambdathrowingMerger()与之相同,但我不能直接调用它,因为它是私有包(您当然可以总是像这样按自己的方式创建静态方法throwingMerger()。)


4
正如字母所暗示的那样,您指定的参数k不是key,而是用于合并的二进制操作的第一个值。
antak

1
@antak的javadoc令人困惑。但是我实际上从Collectors#throwingMerger接收了异常消息,该消息对第一个参数说“ duplicate key”
dkatzel


1
该SO-answer:stackoverflow.com/questions/25712591/…通过简单地假设没有重复项(如果存在,则覆盖),建议了一种更短的放置方法。
mortensi

4
我从更改(k,v)(v1,v2),因为lambda参数实际上是两个冲突的值。在throwingMerger()JDK中是错误的。我希望你不要介意。:)
ChristofferHammarström'16

8

基于dkatzel的确认,即没有一个好的API方法,我选择维护自己的自定义Collectors类:

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);
    }

}

1
您应该更改异常消息。您可以使用其中一个值(就像JDK一样:bugs.openjdk.java.net/browse/JDK-8040892),但是消息提示这是关键。可以显示密钥(hg.openjdk.java.net/jdk9/dev/jdk/rev/8b80651ce43f),但这比较复杂,所以也许只需使用即可throw new IllegalStateException(String.format("Duplicate key for values %s and %s", u, v));
马丁

嘿@马丁,谢谢你的链接!在示例中,我试图从类的私有throwingMerger方法中逐字地发布代码,Collectors以表明我正在解决私有问题。我完全知道您来自哪里,并且出现的错误消息更好,但也许会降低尝试说“做的完全相同的事情,可见性修改器正在起作用”的意图吗?我如何将您的建议添加为底部的编辑内容并引用您的来源呢?
罗伯特·贝恩


5

执行此操作的另一种方法是允许Collectors.toMap()返回将要返回的任何地图,然后将其传递给新的TreeMap <>()。

需要注意的是,这仅在您的“ hashCode()+ equals()”和“ compareTo”一致时才有效。如果它们不一致,那么您最终将获得HashMap删除与TreeMap不同的键集。


3

如果您使用番石榴库,则可以使用:

.collect(ImmutableSortedMap.toImmutableSortedMap(comparator, keyMapper, valueMapper));

生成的地图将是SortedMap且也是不可变的。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.