如何使用 java 流对字段进行分组并创建摘要

how to use java stream to group fields and create a summary

public class Call {
    private String status;
    private String callName;
}

我有一个通话列表,我必须创建一个摘要,如下所示:

public class CallSummary {
    private String callName;
    private List<ItemSummary> items;
}
public class itemSummary {
    private String status;
    private Integer percentage;
}

我的目标是显示一定比例的具有某种状态的呼叫 喜欢 :

INBOUND_CALL : {
FAILED = 30%
SUCCESS = 70%
}

我如何使用 java 8 流和收集器来做到这一点?

以下内容收集到状态 -> 百分比图,然后您可以将其转换为输出模型。此代码采用 getStatus 方法。

List<Call> calls;
Map<String,Double> statusPercents = calls.stream()
    .collect(Collectors.groupingBy(Call::getStatus,
        Collectors.collectingAndThen(Collectors.counting(), 
            n -> 100.0 * n / calls.size())));

我发现这段代码有点难读。收集器链按状态对呼叫进行分组,然后对每个组进行计数,最后转换为百分比。您可以(可以说)通过为收集器设置临时变量来使其更具可读性:

var percentFunction = n -> 100.0 * n / calls.size();
var collectPercent = collectingAndThen(count(), percentFunction);
var collectStatusPercentMap = groupingBy(Call::getStatus, collectPercent);

您还想按呼叫名称分组,但这实际上是一回事 - 使用 groupingBy 然后将呼叫列表减少到 CallSummary.

分组背后的想法是以这样一种方式嵌套,即您有一个呼叫名称,然后可以使用基于状态的计数查找。我还建议对状态使用枚举

enum CallStatus {
    FAILED, SUCCESS
}

并在其他 类 中将其改编为

class Call {
    private CallStatus status;
    private String callName;
}

然后您可以实现嵌套分组并从中间结果开始,例如:

List<Call> sampleCalls = List.of(new Call(CallStatus.SUCCESS,"naman"),new Call(CallStatus.FAILED,"naman"),
        new Call(CallStatus.SUCCESS,"diego"), new Call(CallStatus.FAILED,"diego"), new Call(CallStatus.SUCCESS,"diego"));

Map<String, Map<CallStatus, Long>> groupedMap = sampleCalls.stream()
        .collect(Collectors.groupingBy(Call::getCallName,
                Collectors.groupingBy(Call::getStatus, Collectors.counting())));

这会给你

的输出
{diego={FAILED=1, SUCCESS=2}, naman={FAILED=1, SUCCESS=1}}

您还可以进一步评估百分比。 (尽管在 Integer 中表示它们可能会失去精度,具体取决于您如何进一步评估它们。)


为了进一步解决它,您可以保留另一个 Map 用于基于名称的计数查找:

Map<String, Long> nameBasedCount = calls.stream()
        .collect(Collectors.groupingBy(Call::getCallName, Collectors.counting()));

并进一步计算 List 中类型 CallSummary 的摘要:

List<CallSummary> summaries = groupedMap.entrySet().stream()
        .map(entry -> new CallSummary(entry.getKey(), entry.getValue().entrySet()
                .stream()
                .map(en -> new ItemSummary(en.getKey(), percentage(en.getValue(),
                        nameBasedCount.get(entry.getKey()))))
                .collect(Collectors.toList()))
        ).collect(Collectors.toList());

其中 percentage 计数由您使用与 ItemSummary 中选择的数据类型对齐的签名 int percentage(long val, long total) 实现。

示例结果:

[
CallSummary(callName=diego, items=[ItemSummary(status=FAILED, percentage=33), ItemSummary(status=SUCCESS, percentage=66)]), 
CallSummary(callName=naman, items=[ItemSummary(status=FAILED, percentage=50), ItemSummary(status=SUCCESS, percentage=50)])
]