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
完全不同的实现,因此 toMap
和 toConcurrentMap
完全重写了没有合并功能的收集器。
要求抛出合并功能的一个常见原因是,没有 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));
}
我正在实施自己的收集器,该收集器使用 合并功能。不幸的是,对于我的某些情况,我无法重用以下抛出 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
完全不同的实现,因此 toMap
和 toConcurrentMap
完全重写了没有合并功能的收集器。
要求抛出合并功能的一个常见原因是,没有 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));
}