1. 复现错误
今天,测试在禅道上给我指出一个正式环境bug
,如下图所示:
即java.lang.IllegalStateException: Duplicate key 2
。
2. 分析错误
根据java.lang.IllegalStateException: Duplicate key 2
可知,这是java
抛出的存在2个重复键
的错误。
如果想要弄清楚错误原因,通过如下几个步骤分析:
- 首先,看到这个错误信息,先在本地使用
postman
测试这个接口,如下图所示:
由于涉及到公司的安全,不能把接口展示出来。
由上图可知,本地环境没有问题。
- 查看正式环境的日志,如下图所示:
由错误的第2行
可知,这是stream.Collectors
引起出的错误。
继续往下看,找到红框处,这是项目代码错误抛出的的位置。
于是,查看线上的代码如下所示:
xxxRepository.list(new xxxQuery().setXxx(xxxId))
.stream()
.collect(Collectors.toMap(xxxEntity::getName, xxxEntity::getId));
由于保密机制,把实际名称,用xxx
替换了。
由xxxEntity::getName
代码猜想:可能是由于数据库存在两个相同的名字,在转化成Map
时就会存在相同的Key
,从而导致hash
异常,如下代码所示:
@Data
@NoArgsConstructor
public class Student {
private BigDecimal score;
private String name;
private Integer id;
private Integer age;
@Override
public String toString() {
return JSON.toJSONString(this);
}
public Student(BigDecimal score, String name, Integer id, Integer age) {
this.score = score;
this.name = name;
this.id = id;
this.age = age;
}
public static void main(String[] args) {
Student student1 = new Student(new BigDecimal(99.5), "陈希尔", 1, 12);
Student student2 = new Student(new BigDecimal(88), "陈希尔", 2, 12);
List<Student> list = new ArrayList();
list.add(student1);
list.add(student2);
Map<String, Integer> collect =
list.stream().collect(Collectors.toMap(Student::getName, Student::getId));
Set<String> keys = collect.keySet();
String sout = "key = %s, value = %s \n";
keys.forEach(t -> System.out.format(sout, t, collect.get(t)));
}
}
注意上述的示例代码,下文也是基于此示例代码修改。
输出结果如下图所示:
- 根据猜想到数据库中查询数据,如下所示:
select
COUNT(`name`) as nameCount,
`name`
from
xxx_table
GROUP BY
`name`
HAVING
nameCount >= 2;
找到了问题的所在,原来是name
重复了,导致这个错误。
在实际应用开发中,我们会常把一个List
的查询数据集合转为一个Map
,那么在这里的 list.stream().collect()
其实就是做了这么一件事情,它是java8
的stream
方式实现的它是以type
为key
,以entity
对象为value
构成Map
。
本错误中的type
就是name
,而map
中的key
值是不能重复的,重复会导致hash
异常。
3. 解决问题
既然知道了问题的原因,我们可以用如下方式解决:
-
删除
name
重复记录 -
修改代码,保证即便有重复的值,也能转化成
Map
。
我们只需要将这行代码:
Map<String, Integer> collect =
list.stream().collect(Collectors.toMap(Student::getName, Student::getId));
按如下方式修改即可:
Map<String, Integer> collect =
list.stream()
.collect(
Collectors.toMap(Student::getName, Student::getId, (entity1, entity2) -> entity1));
此代码等效于:
Map<String, Integer> collect =
list.stream()
.collect(
Collectors.toMap(
Student::getName,
Student::getId,
new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer entity1, Integer entity2) {
return entity1;
}
}));
修改完后,如下为测试结果:
4. 重要补充
Map<String, Integer> collect =
list.stream()
.collect(
Collectors.toMap(Student::getName, Student::getId, (entity1, entity2) -> entity1));
等效于:
Map<String, Integer> collect =
list.stream()
.collect(
Collectors.toMap(
Student::getName,
Student::getId,
new BinaryOperator<Integer>() {
@Override
public Integer apply(Integer entity1, Integer entity2) {
return entity1;
}
}));
我们去看toMap
的源代码就知道了,如下图所示:
文章来源:https://www.toymoban.com/news/detail-804187.html
它第三个参数就是函数式接口,因而,我们可以使用lamda
表达式来简化代码。文章来源地址https://www.toymoban.com/news/detail-804187.html
到了这里,关于详细解决java中的Collectors.toMap引起的java.lang.IllegalStateException: Duplicate key xxx 的错误的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!