You need to flatten the list of roles obtained from the map roleByUsername
.
By the way, there is an inconsistency: according to its name the map is meant to contain usernames as keys, but in the loop you're accessing it by providing the result returned by the call getPassword()
.
Then you need to apply collect
as a terminal operation and provide collector toMap()
as an argument in order to generate a map which associates roleId
with a single user.
List<Role> roles = // initialing the list of roles
Map<String, List<Role>> roleByUsername = roles.stream()
.collect(Collectors.groupingBy(Role::getRoleId));
List<User> users = List.of(new User(), new User());
Map<String, User> userByRoleId = users.stream()
.flatMap(user -> roleByUsername.get(user.getPassword()).stream()
.map(role -> Map.entry(role.getRoleId(), user)))
.collect(Collectors.toMap(
Map.Entry::getKey,
Map.Entry::getValue
));
But there's a logical problem here. Assuming that roleId
is unique for every role, but there could multiple users having the same role. Unless you have designed an application where every user has a unique role.
Therefore, it makes sense to change the map type to Map<String, List<User>>
so that a collection of user would be mapped to each roleId
.
And to achieve that, we need to change the collector from toMap
to groupingBy()
in conjunction with mapping()
and toList()
.
Map<String, List<User>> userByRoleId = users.stream()
.flatMap(user -> roleByUsername.get(user.getPassword()).stream()
.map(role -> Map.entry(role.getRoleId(), user)))
.collect(Collectors.groupingBy(
Map.Entry::getKey,
Collectors.mapping(Map.Entry::getValue,
Collectors.toList())
));
Sidenotes:
Collector groupingBy()
has a flavor that expects only one argument - classifier
function. It will store all elements mapped to the same key into a list by default. You don't need to provide toList()
as a downstream.
Write your code against interfaces like Map
, don't make it dependent on concrete implementations: What does it mean to "program to an interface"?