先把退款转换成负数,再用 collectors.groupingby 按客户分组,并用 summinglong 求和。金额用整数分表示,正的汇总值表示净回款,负值表示退款超过回款。

先统一回款与退款的符号
将代码保存为 NetPayments.java,用 Java 17 或更高版本运行。输入金额字段始终存正数,由交易类型决定符号。
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.stream.Collectors;
public class NetPayments {
enum Kind { RECEIPT, REFUND }
record Entry(String customer, Kind kind, long cents) {
long signedCents() {
return kind == Kind.REFUND ? -cents : cents;
}
}
public static void main(String[] args) {
var entries = List.of(
new Entry("A", Kind.RECEIPT, 5000),
new Entry("B", Kind.REFUND, 1200),
new Entry("A", Kind.REFUND, 1500),
new Entry("A", Kind.RECEIPT, 2000)
);
Map<String, Long> totals = entries.stream().collect(
Collectors.groupingBy(Entry::customer, TreeMap::new,
Collectors.summingLong(Entry::signedCents))
);
System.out.println(totals);
}
}
执行 java NetPayments.java,预期得到 {A=5500, B=-1200}。TreeMap 让打印顺序按客户 ID 排列。
立即学习“Java免费学习笔记(深入)”;
核对合计与原始流水是否一致
在 main 末尾追加校验,对比按客户汇总后的合计和逐笔带符号的合计。
long fromGroups = totals.values().stream().mapToLong(Long::longValue).sum();
long fromEntries = entries.stream().mapToLong(Entry::signedCents).sum();
if (fromGroups != fromEntries || fromGroups != 4300) {
throw new IllegalStateException("流水与分组汇总不一致");
}正确数据的总净回款是 4300 分。线上数据应先限制币种、结算周期和金额范围,再执行汇总。
提取退款大于回款的客户
接着从汇总结果里筛选负值。
var refundHeavy = totals.entrySet().stream()
.filter(entry -> entry.getValue() < 0)
.map(Map.Entry::getKey)
.toList();
System.out.println(refundHeavy);预期输出 [B]。若金额可能超出 long 的求和范围,可以改用 BigInteger 或 BigDecimal 聚合。


















