【问题标题】:Java stream map of map of list列表map之Java流图
【发布时间】:2021-09-21 00:53:39
【问题描述】:

我有一个看起来像这样的实体:

public class Snippet {
    private Integer docId;
    private Integer page;
    private Payload payload;
}

输入数据是List<Snippet>

我需要创建一个索引,允许我们遍历 docId 和页面并获取相关的 Snippet 对象。

所以数据结构是这样的:

Map<Integer, Map<Integer, List<Snippet>>>

我可以使用 Java 流来获取 Map> - 但这不会在最后收集 Snippets 列表。

        List<Snippet> input = ....;
        input.stream()
        .collect(Collectors.groupingBy(
                    Snippet::getDocId, 
                    Collectors.toMap(Snippet::getPage, Function.identity())
                 )
        );

如何进行收集以获取 List 作为最终地图值?

【问题讨论】:

    标签: java java-stream


    【解决方案1】:

    如您所见,Collectors.groupingBy(..) 将收集器作为第二个参数。所以,只需使用Collectors.groupingBy(..) 作为Collectors.groupingBy(..) 的第二个参数

    input.stream().collect(Collectors.groupingBy(
                            Snippet::getDocId,
                            Collectors.groupingBy(Snippet::getPage,
                                    Collectors.toList())));
    

    【讨论】:

    • 谢谢 - 现在我读到它似乎很明显。我想我已经挂断了收集到地图的需要 - 但我猜 groupingBy 收集器自己创建了一个地图。
    【解决方案2】:

    这是您当前或未来考虑的另一种选择。它使用Map InterfacecomputeIfAbsent 方法。

    • 创建最终的内容映射。
    • 遍历Snippets列表
    • 如果DocId 不存在,则使用DocId 作为键创建条目。然后尝试存储pageSnippet
    • 如果page 不存在,则使用page 作为键创建条目。然后将Snippet 存储在列表中。

    这是一个使用记录代替类的示例。

    record PayLoad(String getValue) {
        @Override
        public String toString() {
            return "[" + getValue() + "]";
        }
    }
    
    record Snippet(int getDocId, int getPage,
            PayLoad getPayLoad) {
        @Override
        public String toString() {
            return String.format("{%s, %s, %s}", getDocId,
                    getPage, getPayLoad);
        }
    }
    

    创建一些数据

    List<Snippet> input =
            List.of(new Snippet(1, 1, new PayLoad("A")),
                    new Snippet(1, 2, new PayLoad("B")),
                    new Snippet(1, 3, new PayLoad("C")),
                    new Snippet(2, 1, new PayLoad("D")),
                    new Snippet(2, 2, new PayLoad("E")),
                    new Snippet(2, 3, new PayLoad("F")));
    

    存储sn-ps

    Map<Integer, Map<Integer, List<Snippet>>> map =
            new HashMap<>();
    
    for (Snippet s : input) {
        map.computeIfAbsent(s.getDocId(), v -> new HashMap<>())
                .computeIfAbsent(s.getPage(),
                        v -> new ArrayList<>())
                .add(s);
    }
    

    打印出来

    map.forEach((k, v) -> {
        System.out.println(k);
        v.forEach((kk, vv) -> System.out
                .println("          " + kk + " -> " + vv));
    });
    

    打印

    1
              1 -> [{1, 1, [A]}]
              2 -> [{1, 2, [B]}]
              3 -> [{1, 3, [C]}]
    2
              1 -> [{2, 1, [D]}]
              2 -> [{2, 2, [E]}]
              3 -> [{2, 3, [F]}]
    

    【讨论】:

    • Java 17 的最低版本?
    • 一点也不。这些记录只是为了简化示例,但最终在 Java 16 中完成。映射是 Java 8,因此实际的生产运行将使用 Java 8+。
    • 这是一个非常聪明的解决方案 - 谢谢。 computeIfAbsent 为流操作过于复杂的情况编写了一些非常优雅的代码。感谢您的超越 - 非常感谢。
    猜你喜欢
    • 2019-02-17
    • 2015-09-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-30
    • 2016-08-18
    • 2019-05-24
    • 1970-01-01
    相关资源
    最近更新 更多