返回

Collectors.toMap引起的java.lang.IllegalStateException: Duplicate key xxx解决方法

后端

Collectors.toMap 引发的 java.lang.IllegalStateException: Duplicate key:详解及其解决方法

Collectors.toMap 是 Java 中一个非常有用的方法,它可以将一个列表转换为一个 Map。但是,在使用此方法时,可能会遇到 java.lang.IllegalStateException: Duplicate key 错误。

原因

此错误的原因是,Collectors.toMap 方法在转换过程中遇到了重复的键。当一个列表中有多个元素具有相同的键时,就会发生这种情况。

解决方案

有几种方法可以解决此错误:

1. 使用 mergeFunction 解决重复键

Collectors.toMap 方法提供了一个 mergeFunction 参数,可以用来指定如何处理重复的键。我们可以使用 mergeFunction 将重复键的值合并为一个新的值,从而避免错误的发生。

List<Item> items = new ArrayList<>();
items.add(new Item(1, "A"));
items.add(new Item(2, "B"));
items.add(new Item(1, "C"));

Map<Integer, String> map = items.stream()
        .collect(Collectors.toMap(Item::getId, Item::getName, (oldValue, newValue) -> oldValue + ", " + newValue));

System.out.println(map);

输出结果:

{1=A, C, 2=B}

2. 使用 groupingBy 解决重复键

Collectors.groupingBy 方法可以将列表中的元素分组,并返回一个 Map,其中键是分组的键,值是分组后的元素列表。我们可以使用 groupingBy 方法将列表中的元素分组,然后使用 Collectors.mapping 方法将分组后的元素列表转换为 Map。

List<Item> items = new ArrayList<>();
items.add(new Item(1, "A"));
items.add(new Item(2, "B"));
items.add(new Item(1, "C"));

Map<Integer, List<String>> map = items.stream()
        .collect(Collectors.groupingBy(Item::getId, Collectors.mapping(Item::getName, Collectors.toList())));

System.out.println(map);

输出结果:

{1=[A, C], 2=[B]}

3. 使用 Collector.of 解决重复键

我们可以使用 Collector.of 方法来创建一个自定义的收集器,该收集器可以处理重复的键。

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collector;

public class CustomCollector {

    public static <T, K, V> Collector<T, ?, Map<K, V>> toMap(Function<? super T, ? extends K> keyMapper,
                                                                Function<? super T, ? extends V> valueMapper,
                                                                BinaryOperator<V> mergeFunction) {
        return Collector.of(HashMap::new, (map, t) -> {
            K key = keyMapper.apply(t);
            V oldValue = map.putIfAbsent(key, valueMapper.apply(t));
            if (oldValue != null) {
                map.put(key, mergeFunction.apply(oldValue, valueMapper.apply(t)));
            }
        }, (map1, map2) -> {
            map1.putAll(map2);
            return map1;
        });
    }

    public static void main(String[] args) {
        List<Item> items = new ArrayList<>();
        items.add(new Item(1, "A"));
        items.add(new Item(2, "B"));
        items.add(new Item(1, "C"));

        Map<Integer, String> map = items.stream()
                .collect(toMap(Item::getId, Item::getName, (oldValue, newValue) -> oldValue + ", " + newValue));

        System.out.println(map);
    }
}

输出结果:

{1=A, C, 2=B}

常见问题解答

  • 问:为什么我会遇到 java.lang.IllegalStateException: Duplicate key 错误?
    • 答:因为 Collectors.toMap 方法在转换过程中遇到了重复的键。
  • 问:如何解决 java.lang.IllegalStateException: Duplicate key 错误?
    • 答:可以通过使用 mergeFunctiongroupingByCollector.of 方法来解决此错误。
  • 问:哪个方法是处理重复键的最佳方法?
    • 答:这取决于具体情况。mergeFunction 方法允许合并重复键的值,而 groupingBy 方法将重复键分组为列表。Collector.of 方法提供了一种创建自定义收集器的灵活性。
  • 问:Collectors.toMap 方法还有哪些用途?
    • 答:Collectors.toMap 方法可以用来将列表转换为各种不同的 Map,例如,可以根据元素的键分组元素的 Map,或者根据元素的键和值创建 Map。
  • 问:Collectors.toMap 方法的局限性是什么?
    • 答:Collectors.toMap 方法的一个局限性是它无法处理 null 键或值。