如何使用 BinaryOperator 对对象流使用 Java reduce() 函数

How to use Java reduce() function on stream of objects with BinaryOperator

combinedResult 是一个 ObjectNode 类型的流,由如下数据组成,例如:

{"id":"id1","version":1,"Success":4,"Exception":6,"Failure":3}
{"id":"id1","version":1,"Success":4,"Exception":6,"Failure":3}
{"id":"id1","version":2,"Exception":1,"Success":2,"Failure":2}
{"id":"id1","version":2,"Exception":1,"Success":2,"Failure":2}

我想得到这样的结果:

{"id":"id1","version":1,"Success":8,"Exception":12,"Failure":6}
{"id":"id1","version":2,"Success":4,"Exception":2,"Failure":4}

我写了下面的 BinaryOperator 函数

BinaryOperator<ObjectNode> func = (o1, o2) -> {
        if (o1.get("id").asText().equals(o2.get("id").asText()) &&
                o1.get("version").equals(o2.get("version"))) {
            ObjectNode o = Jive.newObjectNode();
            o.set("id", Jive.newJsonNode(o1.get("id")));
            o.set("version", Jive.newJsonNode(o1.get("version")));
            o.set("Success", Jive.newJsonNode(o1.get("Success").asInt() + o2.get("Success").asInt()));
            o.set("Failure", Jive.newJsonNode(o1.get("Failure").asInt() + o2.get("Failure").asInt()));
            o.set("Exception", Jive.newJsonNode(o1.get("Exception").asInt() + o2.get("Exception").asInt()));
            return o;
        }
        return o1;
    };

combinedResult.stream().reduce(func)

但是当我尝试这个时,我得到以下结果:

Optional[{"id":"id1","version":1,"Success":8,"Failure":6,"Exception":12}]

我知道这是因为我在 BinaryOperator 中将 return o1 作为默认值,但我不知道如何解决这个问题。

你可以使用reduce方法。

    <U> U reduce(U identity,
                     BiFunction<U, ? super T, U> accumulator,
                     BinaryOperator<U> combiner);

您的初始身份是一个空的 HashMap,其键将是您要在其上累积结果的唯一标识符。 (ID + 版本)

public class JavaReduce {
    public static void main(String[] args) {
        //DataSetup
        List<ObjectNode> objectNodeList = List.of(
                new ObjectNode("id1", 1, 4, 6, 3),
                new ObjectNode("id1", 1, 4, 6, 3),
                new ObjectNode("id2", 2, 2, 1, 2),
                new ObjectNode("id2", 2, 2, 1, 2));

        Map<String, ObjectNode> objectNodeCumulativeMap = objectNodeList.stream()
                .reduce(new HashMap<>(), (intermediate, ObjectNode) -> {
                    String key = ObjectNode.getId().concat(String.valueOf(ObjectNode.getVersion()));
                    if(!intermediate.containsKey(key)){
                        intermediate.put(key, ObjectNode);
                    } else {
                        ObjectNode objectNode = intermediate.get(key);
                        objectNode.setSuccess(objectNode.getSuccess() + ObjectNode.getSuccess());
                        objectNode.setFailure(objectNode.getFailure() + ObjectNode.getFailure());
                        objectNode.setException(objectNode.getException() + ObjectNode.getException());
                    }
                    return intermediate;
                }, (cumulative, intermediate) -> {
                    cumulative.putAll(intermediate);
                    return cumulative;
                });

        System.out.println(objectNodeCumulativeMap.values());
    }
}

//DTO for data
@Data
@AllArgsConstructor
@NoArgsConstructor
class ObjectNode {
    private String id;
    private Integer version;
    private Integer success;
    private Integer exception;
    private Integer failure;
}

但是 Sudipta Bhattacharyya 和 Holger 提到的理想方法是使用 collect

下面是解决方案的片段。

public class JavaCollect {
    public static void main(String[] args) {
        //DataSetup
        List<ObjectNode> objectNodeList = List.of(
                new ObjectNode("id1", 1, 4, 6, 3),
                new ObjectNode("id1", 1, 4, 6, 3),
                new ObjectNode("id2", 2, 2, 1, 2),
                new ObjectNode("id2", 2, 2, 1, 2));

        Map<String, ObjectNode> collect = objectNodeList.stream()
                .collect(groupingBy(JavaCollect::uniqueKey, collectingAndThen(toList(), JavaCollect::downstream)));

        System.out.println(collect.values());

    }

    private static ObjectNode downstream(List<ObjectNode> list) {
        ObjectNode objectNode = list.stream().findFirst().orElse(new ObjectNode());
        objectNode.setSuccess(list.stream().map(ObjectNode::getSuccess).collect(summingInt(Integer::intValue)));
        objectNode.setException(list.stream().map(ObjectNode::getException).collect(summingInt(Integer::intValue)));
        objectNode.setFailure(list.stream().map(ObjectNode::getFailure).collect(summingInt(Integer::intValue)));
        return objectNode;
    }

    private static String uniqueKey(ObjectNode objectNode) {
        return objectNode.getId().concat(objectNode.getVersion().toString());
    }
}