Java流 - 组合并返回嵌套地图

发布于 2025-01-29 04:44:31 字数 3084 浏览 4 评论 0原文

我的数据就是这样,

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

我的目标是将每个unitid和价值放入这样的地图中,

{
  '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或其他东西?

my data is like this,

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

my goal is put every unitId and value into a map like this,

{
  '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},
}

I already figure out two ways to achieve that, here is my code,

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;
    }

}

and,

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);
    }
}

First one more like write the processing manually, second one uses temporary lists which may not be necessary. Is there any direct or simple way to do this, like use Collectors.toMap API or something else?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

乞讨 2025-02-05 04:44:31

是否有任何直接或简单的方法来执行此操作,例如使用Collector.Tomap API或其他方法?

如果您只想使用内置的收集器,则可以尝试groupingby() and teeing()

collector.teeing()期望三个参数2 downstream collectors 合并 函数。流中的每个元素都将传递到两个 collector 中,当完成这些收集器时,它们产生的结果将通过函数合并。

在下面的代码中,tomap()用作teeing()下游收集器。这些收集器均负责检索其 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}

注意:

  • 如果涉及性能,collector.of()会更好,因为它不喜欢t创建中间收藏。
  • 对于这种方法正确工作的方法(我的意思是上面列出的代码以及问题),unitID的每种组合应该是唯一的。否则,请考虑添加用于解决重复项的逻辑。

Is there any direct or simple way to do this, like use Collectors.toMap API or something else?

If you want to utilize only built-in collectors, you might try a combination of groupingBy() and teeing().

Collectors.teeing() expects three arguments: 2 downstream collectors and a merger function. Each element from the stream will be passed into both collectors, and when these collectors are done, results produced by them will get merged by the function.

In the code below, toMap() is used as both downstream collectors of teeing(). And each of these collectors is responsible for retrieving its type of value.

The code might look like that:

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:

  • If performance is concerned, Collector.of() would be slightly better because it doesn't create intermediate collections.
  • For this approach to work correctly (I mean the code listed above as well as in the question), each combination of unitId and year should be unique. Otherwise, consider adding a logic for resolving duplicates.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文