Вы можете создать ключевой класс, содержащий три поля, например
@Data
class UserKey {
String firstName;
String lastName;
String type;
static UserKey from(User user) { /* TODO (trivial) */ }
}
groupingBy
Они могут использоваться для группировки ваших пользователей
Map<UserKey,List<User>> grouped =
users.stream().collect(Collectors.groupingBy(UserKey::from));
Каждый из этих списков затем может быть объединен с помощью
Optional<User> summed = userList.stream()
.collect(Collectors.reducing((u1, u2) -> {
u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
});
Это также может быть передано непосредственно как нижестоящий коллектор в groupingBy
:
Map<UserKey,Optional<User>> mergedMap =
users.stream().collect(Collectors.groupingBy(UserKey::from,
Collectors.reducing((u1, u2) -> {
u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
return u1;
}));
Поскольку эти Optional
s гарантированно будут заполнены, вы можете просто позвонить get()
на них; кроме того, вам больше не нужны ключи, поэтому
List<User> result = mergedMap.values().stream()
.map(Optional::get)
.collect(toList());
toMap
Как предложил Наман в комментариях, вы также можете откорректировать это с помощью toMap
.
Map<UserKey,User> mergedMap = users.stream()
.collect(toMap(UserKey::from, Function.identity(),
(u1, u2) -> {
u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
return u1;
}));
List<User> result = new ArrayList<>(mergedMap.values());
Обратите внимание, что у функции сокращения есть побочный эффект манипуляции с одним из исходных пользовательских объектов в списке, поэтому убедитесь, что они вам больше не нужны.