一 故事背景
今天乐乐和同事们一起发现了一个异常
Exception in thread "main" java.lang.IllegalStateException: Duplicate key
引发异常的代码原因是Collectors.toMap
中出现了重复的key, 于是乐乐为了研究问题的发生原因自己开始撸代码测试
二 异常分析
乐乐首先创建了一个集合用于测试
List list = Lists.newArrayList();
list.add("3");
list.add("4");
list.add("5");
list.stream().forEach(s-> System.out.println(s));
乐乐发现有另外一种更简便的方式创建集合
Arrays.asList("3","4","5").stream().forEach(s-> System.out.println(s));
简洁了不少,但是乐乐突然又发现还有比这种还简洁的
Stream.of("3","4","5").forEach(s-> System.out.println(s));
接着我们看一下上面的问题
Map collect = Stream.of("a", "b", "c", "a").collect(Collectors.toMap(x -> x, x -> x + x));
collect.forEach((k,v) -> System.out.println(k + ":" + v));
执行这段代码机会发生上面的异常,如何解决呢?
三 解决方案
增加合并函数,合并函数是什么呢?看下源码调用
public static Collector<T, ?, Map> toMap(Function keyMapper,
Function valueMapper,
BinaryOperator mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
BinaryOperator mergeFunction
就是合并函数
@FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> {
/**
* Returns a {@link BinaryOperator} which returns the lesser of two elements
* according to the specified {@code Comparator}.
*
* @param <T> the type of the input arguments of the comparator
* @param comparator a {@code Comparator} for comparing the two values
* @return a {@code BinaryOperator} which returns the lesser of its operands,
* according to the supplied {@code Comparator}
* @throws NullPointerException if the argument is null
*/
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
/**
* Returns a {@link BinaryOperator} which returns the greater of two elements
* according to the specified {@code Comparator}.
*
* @param <T> the type of the input arguments of the comparator
* @param comparator a {@code Comparator} for comparing the two values
* @return a {@code BinaryOperator} which returns the greater of its operands,
* according to the supplied {@code Comparator}
* @throws NullPointerException if the argument is null
*/
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
}
他继承了另一个函数式接口
@FunctionalInterface
public interface BiFunction<T, U, R> {
/**
* Applies this function to the given arguments.
*
* @param t the first function argument
* @param u the second function argument
* @return the function result
*/
R apply(T t, U u);
/**
* Returns a composed function that first applies this function to
* its input, and then applies the {@code after} function to the result.
* If evaluation of either function throws an exception, it is relayed to
* the caller of the composed function.
*
* @param <V> the type of output of the {@code after} function, and of the
* composed function
* @param after the function to apply after this function is applied
* @return a composed function that first applies this function and then
* applies the {@code after} function
* @throws NullPointerException if after is null
*/
default <V> BiFunction<T, U, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t, U u) -> after.apply(apply(t, u));
}
}
java8中接口不只只有default
方法可以写实现,stastic
方法也可以写实现。
回到合并函数,合并函数可以让map不出现重复的key值,所以不回报错,但是他本身会改变key值这一点是要十分小心的。
修改后
Map collect = Stream.of("a", "b", "c", "a").collect(Collectors.toMap(x -> x, x -> x + x,(oldVal, newVal) -> newVal));