Java 8 Streams:构建多级/复合对象

时间:2014-12-14 13:14:50

标签: java java-8 java-stream collectors

我开始使用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是userId。

我尝试了各种结构,但最后它比流利的更复杂。并没有工作:) 我在Java 8的书中读过它简单的#34;用收藏家建造dompain对象 当我读到这个时,我哭了一下:&#39;(

我试过

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

并且有一个地狱编译失败的地狱。

  • 使用java 8流构建多级复合域对象的最佳方法是什么?
  • 是收藏和/终结者的好主意吗?
  • 或者你只使用groupingBy后跟一个映射函数?
  • 你是否将分类器转换为一个对象(一级第一级映射函数?)

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

2 个答案:

答案 0 :(得分:6)

让我为您提供一些选择,然后您决定哪种看起来最清楚。我假设User构造函数是User(int userId, List<Permission> permissions),Permission构造函数是Permission(String permissionId, List<Integer> years)

选项1:直接方法。按用户ID分组,构造每个用户标识的权限列表并创建用户对象。就我个人而言,我觉得这很难在收藏家中筑巢,难以理解。

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滚动到userid的映射中,以将permissionid映射到年份列表(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());

答案 1 :(得分:2)

collectingAndThen组合器是为累积的中间形式与所需的最终形式不同而设计的。这是joining()收集器的情况(中间形式为StringBuilder,但最终形式为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”分组(查询的第一个字段)   - 对于每条记录,您选择其余字段的某些功能(这里,为了清楚起见,我使用字符串连接,但您可以创建一个持有权限和年份的对象),并将 发送到下游收集器   - 使用Collectors.toList()作为下游收集器,然后它将是Map创建的groupingBy的“值”。