我正在实现自己的使用 合并功能 的收集器。不幸的是,对于我的某些情况,我无法重用抛出 IllegalStateException 的以下JDK合并函数。
java.util.stream.Collectors#throwingMerger
发生这种情况是因为它具有 私有 访问修饰符,并且限制了其他(而非内部)类的访问。但是,javadoc表示以下内容:
这可以用来执行以下假设:所收集的元素是不同的
但是,正如我所看到的,java doc已经过时了。无法使用。问题是JDK是否为Java开发人员提供了对类似功能的访问(类似方法,常量等),还是应该由他们自己编写?
该throwingMerger()实施如下
throwingMerger()
private static <T> BinaryOperator<T> throwingMerger() { return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); }; }
您可以在代码库中添加类似的方法,但是您应该了解该合并的基本问题:异常消息不正确。该函数的第一个参数是旧值,而不是键。该功能无法使用该键,因此对于此合并功能,不可能生成包含重复键的异常消息。
因此,由于不可能在此位置解决此问题,因此最好将此函数作为实现细节,因此可以在没有任何兼容性约束的情况下将其删除以用于Java 9。
为了提供合理的诊断,toMap不使用合并功能toMap与(使用非抛出)合并功能需要完全不同的实现,因此不使用合并功能的toMapand toConcurrentMap收集器已被完全重写。
toMap
toConcurrentMap
要求使用throwing merge函数的常见原因是,没有合并功能的情况下,没有任何toMap重载接受映射Supplier。但是,由于抛出合并将不会做正确的事,并且当应该拒绝重复密钥时,需要一种完全不同的方法,因此您可以使用此答案的收集器。它的一个稍微改进的版本是
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)); }