Java 8 中 throwingMerger 的替代方案

Alternative for throwingMerger in Java 8

我正在实施自己的收集器,该收集器使用 合并功能。不幸的是,对于我的某些情况,我无法重用以下抛出 IllegalStateException 的 JDK 合并函数。

java.util.stream.Collectors#throwingMerger

发生这种情况是因为它具有 private 访问修饰符并且来自其他(非内部)类 的访问受到限制。 然而,javadoc 说如下:

This can be used to enforce the assumption that the elements being collected are distinct

但是,据我所知,java 文档已过时。它不能被使用。问题是 JDK 是否为 java 开发人员提供了对类似功能的访问(类似的方法、常量等),还是应该自己编写?

throwingMerger()实现如下

private static <T> BinaryOperator<T> throwingMerger() {
    return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
}

您可以将类似的方法添加到您的代码库中,但您应该意识到该合并的根本问题:异常消息不正确。该函数的第一个参数是旧值,而不是键。该键对该函数不可用,因此该合并函数不可能生成包含重复键的异常消息。

因此,既然不可能在这个地方解决这个问题,那么这个函数是一个实现细节是很好的,所以它可以在 Java 9 中删除,没有任何兼容性限制。

为了提供合理的诊断,不带合并功能的 toMap 需要与带(非抛出)合并功能的 toMap 完全不同的实现,因此 toMaptoConcurrentMap 完全重写了没有合并功能的收集器。

要求抛出合并功能的一个常见原因是,没有 toMap 重载接受没有合并功能的映射 Supplier。但是由于抛出合并不会做正确的事情,并且在应该拒绝重复键时需要一种完全不同的方法,您可以改用 的收集器。它的一个稍微改进的版本是

public static <T, K, V, M extends Map<K,V>> Collector<T, ?, M> toMap(
        Function<? super T, ? extends K> keyMapper,
        Function<? super T, ? extends V> valueMapper,
        Supplier<M> mapSupplier) {

    return Collector.of(mapSupplier,
            (m,t) -> putUnique(m, keyMapper.apply(t),
                                  Objects.requireNonNull(valueMapper.apply(t))),
            (m1,m2) -> {
                if(m1.isEmpty()) return m2;
                if(!m2.isEmpty()) m2.forEach((k,v) -> putUnique(m1, k, v));
                return m1;
            });
}
private static <K, V> void putUnique(Map<K, V> map, K key, V v1){
    V v2 = map.putIfAbsent(key, v1);
    if(v2 != null) throw new IllegalStateException(
        String.format("Duplicate key %s (values %s and %s)", key, v1, v2));
}