Java - 在给定条件的情况下合并列表对象

Gui*_*lli 1 java java-stream

我完全相信我的问题很简单,但我无法用流来做(如果有没有流的方法也会有帮助)假设我们有这个用户列表

public class Users {
   String firstName;
   String lastName;
   double accountBalance;
   String type;
   String extraField;
}
Run Code Online (Sandbox Code Playgroud)

并假设我们的 List < Users > 中有以下数据

"Users": [{
            "firstName": "Scott",
            "lastName": "Salisbury",
            "accountBalance": "100",
            "type" : "A"
        }, {
            "firstName": "John",
            "lastName": "Richards",
            "accountBalance": "200",
            "type" :"C"

        }, {
            "firstName": "John",
            "lastName": "Richards",
            "accountBalance": "200",
            "type " : "C",
            "ExtraField": "Apply"
        }]
Run Code Online (Sandbox Code Playgroud)

这里的预期结果是因为 firstName、lastName 和 type 在列表中出现两次,只需合并常见的结果,而不会丢失任何字段
预期输出

"Users": [{
            "firstName": "Scott",
            "lastName": "Salisbury",
            "accountBalance": "100",
            "type" : "A"
        }, {
            "firstName": "John",
            "lastName": "Richards",
            "accountBalance": "400",//merged values
            "type " : "C",
            "ExtraField": "Apply" //value that remains in one object of the list
        }]  
Run Code Online (Sandbox Code Playgroud)

dan*_*niu 5

您可以创建一个包含三个字段的键类,例如

@Data
class UserKey {
    String firstName;
    String lastName;
    String type;

    static UserKey from(User user) { /* TODO (trivial) */ }
}
Run Code Online (Sandbox Code Playgroud)

分组依据

这些可用于对您的用户进行分组

Map<UserKey,List<User>> grouped = 
    users.stream().collect(Collectors.groupingBy(UserKey::from));
Run Code Online (Sandbox Code Playgroud)

然后可以通过以下方式合并这些列表中的每一个

Optional<User> summed = userList.stream()
    .collect(Collectors.reducing((u1, u2) -> {
        u1.setAccountBalance(u1.accountBalance() + u2.accountBalance());
    });
Run Code Online (Sandbox Code Playgroud)

这也可以作为下游收集器直接提供给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;
        }));
Run Code Online (Sandbox Code Playgroud)

由于这些Optionals 保证被填充,你可以调用get()它们;另外,你不再需要钥匙了,所以

List<User> result = mergedMap.values().stream()
                 .map(Optional::get)
                 .collect(toList());
Run Code Online (Sandbox Code Playgroud)

地图

正如 Naman 在评论中建议的那样,您也可以通过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());
Run Code Online (Sandbox Code Playgroud)

请注意,reduce 函数具有操作列表中原始用户对象之一的副作用,因此请确保不再需要它们。