6

我开始使用 java 8 流 API。我想将“sql 结果集”列表转换为域对象,即复合结构。

域对象:一个用户拥有一个权限集合,每个权限都有一个应用年份的集合。例如,John 有 2 个权限(MODERATOR 和 DEV)。其版主权限仅适用于 2014 年和 2015 年,其开发权限仅适用于 2014 年。

class User {
  // some primitives attributes
  List<Permission> permission;
}

class Permission {
  // some primitives attributes
  List<Integer> years;
}

现在我进行查询并得到一个平面结果列表,例如:

[1, "moderator", 2014]
[1, "moderator", 2015]
[1, "dev", 2014]
[2, "dev", 2010]
[2, "dev", 2011]
[2, "dev", 2012]

1 和 2 是用户 ID。

我尝试了各种构造,但最后它比流利的更复杂。并且没有工作:)
我在一本 Java 8 的书中读到,用收集器构建 dompain 对象是“简单的”。
读到这里我有点哭了:'(

我试过

sb.collect(
    collectingAndThen(
        groupingBy(
            Mybean::getUserId,
            collectingAndThen(
                groupingBy(Monbean::getPermissionId, mapping(convertPermission, toList())),
                finisherFonction)
            ),
        convertUser)
);

并得到了一个泛型编译失败的地狱。

  • 使用 java 8 流构造多级复合域对象的最佳方法是什么?
  • collectionAndThen / Finisher 是个好主意吗?
  • 还是您只使用 groupingBy 后跟映射函数?
  • 您是否将分类器转换为对象(某种第一级映射函数?)

因为最后我想摆脱 Map 并得到一个List<User>结果(我想我可以在 entrySet 上添加一个 map 调用来完成转换)。

4

2 回答 2

6

让我为您提供一些选择,您可以决定哪个对您来说最清楚。我假设 User 构造函数是User(int userId, List<Permission> permissions)并且 Permission 构造函数是Permission(String permissionId, List<Integer> years)

选项 1:直接方法。按用户标识分组,为每个用户标识构建权限列表并制作用户对象。就我个人而言,我发现收藏家中有这么多嵌套是很难理解的。

List<User> users = beans.stream()
    .collect(
        groupingBy(
            MyBean::getUserid,
            collectingAndThen(
                groupingBy(
                    MyBean::getPermission,
                    mapping(MyBean::getYear, toList())
                ),
                t -> t.entrySet().stream()
                    .map(e -> new Permission(e.getKey(), e.getValue()))
                    .collect(toList())
            )
        )
    ).entrySet().stream()
    .map(e -> new User(e.getKey(), e.getValue()))
    .collect(toList());

选项 2:与上述相同,但为清楚起见单独制作权限收集器。

Collector<MyBean, ?, List<Permission>> collectPermissions = collectingAndThen(
    groupingBy(MyBean::getPermission, mapping(MyBean::getYear, toList())),
    t -> t.entrySet().stream()
        .map(e -> new Permission(e.getKey(), e.getValue()))
        .collect(toList())
);

List<User> users = beans.stream()
    .collect(groupingBy(MyBean::getUserid, collectPermissions))
    .entrySet().stream()
    .map(e -> new User(e.getKey(), e.getValue()))
    .collect(toList());

选项 3:首先将 bean 滚动到用户 ID 映射到权限 ID 到年份列表 ( Map<Integer, Map<String, List<Integer>>) 的映射。然后从地图中构造领域对象

List<User> users = beans.stream().collect(
    groupingBy(
        MyBean::getUserid,
        groupingBy(
            MyBean::getPermission,
            mapping(MyBean::getYear, toList())
        )
    )
).entrySet().stream()
    .map(u -> new User(
            u.getKey(),
            u.getValue().entrySet().stream()
                .map(p -> new Permission(p.getKey(), p.getValue()))
                .collect(toList())
        )
    ).collect(toList());
于 2014-12-14T22:19:25.627 回答
2

collectingAndThen组合器设计用于累积的中间形式与所需的最终形式不同时。收集器就是这种情况joining()(中间形式是 a StringBuilder,但最终形式是 a String),也可用于在收集完成后用不可变包装器包装可变集合。所以我认为这不是您正在寻找的工具。

如果您正在寻找“用户的权限”,这将起到作用:

Map<UserId, List<Permission>>
    queryResults.stream()
                .collect(qr -> qr.getId(), 
                         mapping(qr -> qr.getPermission() + ":" + qr.getDate(), 
                                 toList()));

这将导致:

1 -> [ moderator:2014, moderator:2015, dev:2014 ]
2 -> [ dev:2010, dev:2011, dev:2012 ]

这个想法是: - 您按“id”(查询的第一个字段)分组 - 对于每条记录,您选择剩余字段的一些功能(在这里,为了清楚起见,我使用了字符串 concat,但您可以创建一个持有的对象权限和年份),并将其发送下游收集器 - 使用 Collectors.toList() 作为下游收集器,这将是Map创建者的“价值” groupingBy

于 2014-12-14T17:44:01.617 回答