Java 8中throwMerger的替代方案

时间:2017-07-19 21:09:06

标签: java java-8 java-stream collectors

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

java.util.stream.Collectors#throwingMerger

它发生的原因是它具有私有访问修饰符,并且限制了来自其他(非内部)类的访问。 但是,javadoc说:

  

这可用于强制假设所收集的元素是不同的

但是,正如我所见,java doc已经过时了。它无法使用。问题是JDK是否为java开发人员提供类似功能(类似方法,常量等),或者应该自己编写?

1 个答案:

答案 0 :(得分:6)

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。但是,由于抛出合并不会做正确的事情,并且当重复密钥被拒绝时需要完全不同的方法,您可以使用this answer的收集器。

是一个稍微改进的版本
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));
}