Let me offer you several options, and you decide which views you most understand. I assume that the constructor of the user is User(int userId, List<Permission> permissions) , and the constructor of Permission is Permission(String permissionId, List<Integer> years)
Option 1: direct approach. Group by user ID, create a list of permissions for each userid, and create User objects. Personally, I think that this many breeding in collectors is difficult to succumb to.
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());
Option 2: Same as above, but make the permission collector separately for clarity.
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());
Option 3: first move the beans to the userid map to map the allowed list to the list of years ( Map<Integer, Map<String, List<Integer>> ). Then build the domain objects off the map
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());