我的数据是这样的,

unitId  time  value1 value2
 a      2021    10    11   
 a      2022    15    13
 b      2021    20    25
 b      2022    30    37

我的目标是把每一个单位和价值都放到这样的 map 上,

{
  'a': {'2021_value1': 10, '2021_value2': 11, '2022_value1': 15, '2022_value2': 13},
  'b': {'2021_value1': 20, '2021_value2': 25, '2022_value1': 30, '2022_value2': 37},
}

我已经想出了两种方法来实现这一点,这是我的代码,

public class Unit {

    public String unitId;

    public Integer year;

    public Integer value1;

    public Integer value2;

    public static Unit of(String unitId, Integer year, Integer value1, Integer value2) {
        Unit unit = new Unit();
        unit.unitId = unitId;
        unit.year = year;
        unit.value1 = value1;
        unit.value2 = value2;
        return unit;
    }

}

public class UnitTest {

    private static void printMap(Map<String, Map<String, Integer>> map) {
        map.forEach((k, v) -> {
            String vStr = v.entrySet().stream().map(a -> String.format("%s: %s", a.getKey(), a.getValue())).collect(Collectors.joining(", "));
            System.out.printf("%s: {%s}%n", k, vStr);
        });
    }

    public static void main(String[] args) {
        List<Unit> list = new ArrayList<>();
        list.add(Unit.of("a", 2021, 10,  11 ));
        list.add(Unit.of("a", 2022, 15,  13));
        list.add(Unit.of("b", 2021, 20,  25));
        list.add(Unit.of("b", 2022, 30,  37));

        Map<String, Map<String, Integer>> map1 = list.stream().collect(
            Collectors.groupingBy(
                x -> x.unitId,
                Collector.of(
                    HashMap::new,
                    (x, y) -> {
                        x.put(String.format("%s_%s", y.year, "value1"), y.value1);
                        x.put(String.format("%s_%s", y.year, "value2"), y.value2);
                    },
                    (x, y) -> {x.putAll(y); return x;}
                )
            )
        );

        Map<String, Map<String, Integer>> map2 = list.stream().collect(
            Collectors.groupingBy(
                x -> x.unitId,
                Collectors.collectingAndThen(
                    Collectors.toList(),
                    x -> x.stream()
                        .flatMap(y -> Stream.of(
                                    new AbstractMap.SimpleEntry<>(String.format("%s_%s", y.year, "value1"), y.value1),
                                    new AbstractMap.SimpleEntry<>(String.format("%s_%s", y.year, "value2"), y.value2)
                             ))
                        .collect(Collectors.toMap(
                                     AbstractMap.SimpleEntry::getKey, 
                                     AbstractMap.SimpleEntry::getValue)))
            )
        );
        printMap(map1);
        printMap(map2);
    }
}

第一种方法更像是手动编写处理,第二种方法使用临时列表,这可能不是必需的.有没有什么直接或简单的方法可以做到这一点,比如使用Collectors.toMap API或其他什么?

推荐答案

有没有直接或简单的方法可以做到这一点,比如使用收集器.toMap API还是别的什么?

如果只想使用built-in collectors,可以try groupingBy()teeing()的组合.

Collectors.teeing()预期三个arguments:2下游collectorsmerger function.流中的每个元素都将被传递到两个collectors,当这些收集器完成时,它们产生的结果将被function合并.

在下面的代码中,toMap()用作teeing()中的downstream collectors.这些collectors中的每一个都负责检索value的类型.

代码可能是这样的:

public static void main(String[] args) {
    List<Unit> list =
        List.of(Unit.of("a", 2021, 10,  11 ),
                Unit.of("a", 2022, 15,  13),
                Unit.of("b", 2021, 20,  25),
                Unit.of("b", 2022, 30,  37));

    Map<String, Map<String, Integer>> map = list.stream()
        .collect(Collectors.groupingBy(Unit::getUnitId,
            Collectors.teeing(
                Collectors.toMap(
                    unit -> unit.getYear() + "_value1",
                    Unit::getValue1),
            Collectors.toMap(
                    unit -> unit.getYear() + "_value2",
                    Unit::getValue2),
                (values1, values2) -> {values1.putAll(values2); return values1;})
        ));

    printMap(map);
}

Output:

a: {2022_value2: 13, 2021_value1: 10, 2022_value1: 15, 2021_value2: 11}
b: {2022_value2: 37, 2021_value1: 20, 2022_value1: 30, 2021_value2: 25}

Note:

  • 如果考虑性能,Collector.of()会稍微好一点,因为它不会创建中间集合.
  • 为了使这种方法正确工作(我指的是上面列出的代码以及问题中列出的代码),unitIdyear的每个组合都应该是唯一的.对于解析逻辑,请考虑添加重复的逻辑.

Java相关问答推荐

在Spring Boot中测试时出现SQL语法错误

Java Streams在矩阵遍历中的性能影响

Javascript更新alert 可扩展内容样式与CSS—按钮更多/更少

Java Swing:初始化身份验证类后未检测到ATM_Interface键事件

如何判断一个矩阵是否为有框矩阵?

内存中的H2修剪尾随空格

无法使用ApacheSpark依赖项构建JavaFX应用程序

Spring和可编辑";where";@Query

如何在Cosmos DB(Java SDK)中增加默认响应大小

来自外部模块的方面(对于Java+Gradle项目)不起作用

根本不显示JavaFX阿拉伯字母

如何在ImageIO或十二只猴子中旋转TIFF CMYK图像?

在Spring Boot JPA for MySQL中为我的所有类创建Bean时出错?

为什么我不能建立输入/输出流?Java ServerSocket

Oracle中从JSON中提取和插入数据

在整数列表中查找和可被第三个整数整除的对时出现无法解释的RunTimeError

在具有Quarkus Panache的PostgreSQL中将JSON数据存储为JSONB时,会将其存储为转义字符串

如何通过用户ID向用户发送私信

整数->;双取消框,但双->;int不';t开箱.为什么?

如何使用Jackson读取以方括号开头的JSON?