1 问题复现
- 问题描述
使用Stream的toMap方法时,key冲突,即相同的key抛异常。 - 代码复现
package lambda_expression;
import common.entity.UserEntity;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Stream临时测试样例.
*
* @author xindaqi
* @date 2021-06-28 18:00
*/
public class StreamTempTest {
private static final Logger logger = Logger.getLogger("StreamTempTest");
public static void main(String[] args) {
List<UserEntity> userEntityList = new ArrayList<>();
userEntityList.add(new UserEntity("1", "111", "male"));
userEntityList.add(new UserEntity("1", "222", "female"));
userEntityList.add(new UserEntity("3", "333", "male"));
Map<String, String> map = Optional.ofNullable(userEntityList).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(UserEntity::getUid, UserEntity::getNickname));
logger.info("Map: " + map);
}
}
- 结果
Exception in thread "main" java.lang.IllegalStateException: Duplicate key 111
at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)
at java.util.HashMap.merge(HashMap.java:1254)
at java.util.stream.Collectors.lambda$toMap$58(Collectors.java:1320)
at java.util.stream.ReduceOps$3ReducingSink.accept(ReduceOps.java:169)
at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1384)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:482)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:472)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at lambda_expression.StreamTempTest.main(StreamTempTest.java:26)
2 原因
2.1 抛异常的toMap方法
toMap两个参数的方法,不会自动处理重复的键,而是直接抛出异常,源码如下:
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper) {
return toMap(keyMapper, valueMapper, throwingMerger(), HashMap::new);
}
- 抛异常
private static <T> BinaryOperator<T> throwingMerger() {
return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
}
2.2 处理重复键的toMap方法
toMap三个参数方法源码如下,通过BinaryOperator处理重复的键。
public static <T, K, U>
Collector<T, ?, Map<K,U>> toMap(Function<? super T, ? extends K> keyMapper,
Function<? super T, ? extends U> valueMapper,
BinaryOperator<U> mergeFunction) {
return toMap(keyMapper, valueMapper, mergeFunction, HashMap::new);
}
- 处理重复键mergeFunction
public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
3 方案
3.1 使用处理重复键的toMap方法
- 保留第一个值
package lambda_expression;
import common.entity.UserEntity;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Stream临时测试样例.
*
* @author xindaqi
* @date 2021-06-28 18:00
*/
public class StreamTempTest {
private static final Logger logger = Logger.getLogger("StreamTempTest");
public static void main(String[] args) {
List<UserEntity> userEntityList = new ArrayList<>();
userEntityList.add(new UserEntity("1", "111", "male"));
userEntityList.add(new UserEntity("1", "222", "female"));
userEntityList.add(new UserEntity("3", "333", "male"));
/**
* 保留第一个值
*/
Map<String, String> map = Optional.ofNullable(userEntityList).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(UserEntity::getUid, UserEntity::getNickname, (oldData, newData) -> oldData));
}
- 保留最后一个值
package lambda_expression;
import common.entity.UserEntity;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Stream临时测试样例.
*
* @author xindaqi
* @date 2021-06-28 18:00
*/
public class StreamTempTest {
private static final Logger logger = Logger.getLogger("StreamTempTest");
public static void main(String[] args) {
List<UserEntity> userEntityList = new ArrayList<>();
userEntityList.add(new UserEntity("1", "111", "male"));
userEntityList.add(new UserEntity("1", "222", "female"));
userEntityList.add(new UserEntity("3", "333", "male"));
/**
* 保留最后一个值
*/
Map<String, String> map = Optional.ofNullable(userEntityList).orElse(new ArrayList<>()).stream().collect(Collectors.toMap(UserEntity::getUid, UserEntity::getNickname, (oldData, newData) -> newData));
logger.info("Map: " + map);
}
}
3.2 使用HashMap::new
package lambda_expression;
import common.entity.UserEntity;
import java.util.*;
import java.util.logging.Logger;
import java.util.stream.Collectors;
/**
* Stream临时测试样例.
*
* @author xindaqi
* @date 2021-06-28 18:00
*/
public class StreamTempTest {
private static final Logger logger = Logger.getLogger("StreamTempTest");
public static void main(String[] args) {
List<UserEntity> userEntityList = new ArrayList<>();
userEntityList.add(new UserEntity("1", "111", "male"));
userEntityList.add(new UserEntity("1", "222", "female"));
userEntityList.add(new UserEntity("3", "333", "male"));
Map<String, String> map = Optional.ofNullable(userEntityList).orElse(new ArrayList<>()).stream().collect(HashMap::new, (k, v) -> k.put(v.getUid(), v.getNickname()), HashMap::putAll);
}