Collectors.toMap() 抛出 “Duplicate key ***” 异常

Collectors.toMap() 抛出 “java.lang.IllegalStateException: Duplicate key ***” 异常。

Collectors.toMap() 抛出 “Duplicate key ***” 异常,异常堆栈如下:

1

2

3

4

5

6

7

8

9

10

11

java.lang.IllegalStateException: Duplicate key ***

    at java.util.stream.Collectors.lambda$throwingMerger$0(Collectors.java:133)

    at java.util.HashMap.merge(HashMap.java:1253)

    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:1374)

    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)

    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)

    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)

错误分析:

这个错误信息表明在使用 Java Stream API 的 Collectors 进行流操作时遇到了一个问题。具体来说,java.util.stream.Collectors.lambda$throwingMerger 指的是在合并收集操作(比如toMap)中的元素时,由于使用了抛出异常的合并器(throwingMerger),而实际上出现了重复的键(key)。java.lang.IllegalStateException: Duplicate key *** 指出键 "***" 在收集过程中重复出现了,而合并策略是抛出 IllegalStateException 异常。

错误示例回放:

下面提供一个错误示例,该示例将抛出上述错误信息,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

package com.hxstrive.demo202406;

 

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.stream.Collectors;

 

public class Demo20240618143339 {

 

    public static void main(String[] args) {

        List<Item> getFunctionList = new ArrayList<>();

        getFunctionList.add(new Item("app1""应用1"));

        getFunctionList.add(new Item("app2""应用2"));

        getFunctionList.add(new Item("app1""应用3"));

 

        // 执行 toMap 操作

        Map<String,String> map = getFunctionList.stream()

                .collect(Collectors.toMap(Item::getAppId, Item::getType));

        System.out.println(map);

    }

 

    // 内部类

    private static class Item {

        private String appId;

        private String type;

 

        public Item(String appId, String type) {

            this.appId = appId;

            this.type = type;

        }

 

        public String getAppId() {

            return appId;

        }

 

        public void setAppId(String appId) {

            this.appId = appId;

        }

 

        public String getType() {

            return type;

        }

 

        public void setType(String type) {

            this.type = type;

        }

    }

 

}

运行上述示例,抛出如下错误信息:

1

2

3

4

5

6

7

8

9

10

11

12

Exception in thread "main" java.lang.IllegalStateException: Duplicate key 应用1

    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:1382)

    at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)

    at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)

    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 com.hxstrive.demo202406.Demo20240618143339.main(Demo20240618143339.java:21)

解决问题:

在 Java Stream API 中,Collectors.toMap 方法用于将流中的元素收集到一个 Map 中。如果你使用的是一个没有指定合并函数的 toMap 重载方法,并且流中的元素有重复的键,那么将会抛出 IllegalStateException 异常,因为默认的合并策略是不允许重复的。

为了解决这个问题,你可以提供一个合并函数,来定义当键冲突时如何合并值。例如:

1

2

3

4

5

Map<KeyType, ValueType> map = stream.collect(Collectors.toMap(

    element -> element.getKey(), // 键的映射函数

    element -> element.getValue(), // 值的映射函数

    (existingValue, newValue) -> existingValue // 合并函数,决定如何处理重复的键

));

上述代码中,合并函数 (existingValue, newValue) -> existingValue 简单地保留了现有的值,并丢弃了新值。你可以根据需要自定义合并函数的行为。

重写示例 Demo20240618143339,代码如下:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

package com.hxstrive.demo202406;

 

import java.util.ArrayList;

import java.util.List;

import java.util.Map;

import java.util.stream.Collectors;

 

public class Demo20240618143339 {

 

    public static void main(String[] args) {

        List<Item> getFunctionList = new ArrayList<>();

        getFunctionList.add(new Item("app1""应用1"));

        getFunctionList.add(new Item("app2""应用2"));

        getFunctionList.add(new Item("app1""应用3"));

 

        Map<String,String> map = getFunctionList.stream()

                .collect(Collectors.toMap(Item::getAppId, Item::getType, 

                                          // 这是合并函数,保留旧值

                                          (oldValue, newValue) -> oldValue));

        System.out.println(map);

    }

 

 

    private static class Item {

        private String appId;

        private String type;

 

        public Item(String appId, String type) {

            this.appId = appId;

            this.type = type;

        }

 

        public String getAppId() {

            return appId;

        }

 

        public void setAppId(String appId) {

            this.appId = appId;

        }

 

        public String getType() {

            return type;

        }

 

        public void setType(String type) {

            this.type = type;

        }

    }

 

}

运行示例,输出如下:

1

{app2=应用2, app1=应用1}

你可能感兴趣的