You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于Invoice属性获取消费最高的10个Taxpayer?

嘿,这个问题我之前做类似需求的时候也碰到过!核心卡点在于你得先把每个纳税人的总消费金额算出来,再基于这个金额排序筛选前10——直接嵌套遍历Taxpayer和Invoice不仅效率低,TreeSet的比较逻辑也没办法直接依赖外部的Invoice数据。我给你梳理下可行的实现思路和代码:

第一步:先预计算每个Taxpayer的总消费金额

首先咱们得把所有发票按TIN归类,算出每个纳税人的总消费,存在一个Map里。这样后面排序的时候不用反复遍历发票集合,效率能提升一大截:

// 计算每个TIN对应的累计消费金额
Map<String, BigDecimal> totalSpendingMap = new HashMap<>();
for (Invoice invoice : this.invoices.values()) {
    String tin = invoice.getTIN();
    BigDecimal amount = invoice.getAmount(); // 假设Invoice有getAmount()返回消费金额
    // 用merge方法自动累加同一TIN的发票金额
    totalSpendingMap.merge(tin, amount, BigDecimal::add);
}

如果某个Taxpayer没有对应的发票,后面可以用getOrDefault把总消费设为0,避免空指针问题。

第二步:按总消费排序,筛选前10个Taxpayer

这里给你两种方案,按需选择:

方案一:用带自定义Comparator的TreeSet(高效维护前10)

如果你坚持要返回TreeSet,可以自定义比较器,结合刚才算的总消费排序。注意TreeSet会根据比较器结果去重,所以总消费相同时,要靠TIN这种唯一标识来区分,避免误删:

// 定义比较器:先按总消费降序,总消费相同则按TIN升序(保证唯一性)
Comparator<Taxpayer> spendingComparator = (t1, t2) -> {
    BigDecimal s1 = totalSpendingMap.getOrDefault(t1.getTIN(), BigDecimal.ZERO);
    BigDecimal s2 = totalSpendingMap.getOrDefault(t2.getTIN(), BigDecimal.ZERO);
    // 降序排列,所以用s2.compareTo(s1)
    int spendingCompare = s2.compareTo(s1);
    if (spendingCompare != 0) {
        return spendingCompare;
    }
    // 总消费相同,用TIN区分,防止TreeSet把不同纳税人当成重复元素
    return t1.getTIN().compareTo(t2.getTIN());
};

TreeSet<Taxpayer> topTen = new TreeSet<>(spendingComparator);
// 遍历所有纳税人,加入TreeSet自动排序,超过10个就移除最小的
for (Taxpayer taxpayer : this.taxpayers.values()) {
    topTen.add(taxpayer);
    if (topTen.size() > 10) {
        topTen.pollLast(); // 移除当前集合中消费最低的元素
    }
}

这种方式不用把所有纳税人都排序,TreeSet会始终维护当前前10的元素,效率更高。

方案二:用List排序后截取前10(逻辑更直观)

如果觉得TreeSet的去重逻辑麻烦,也可以把所有纳税人放到List里排序,再截取前10:

List<Taxpayer> taxpayerList = new ArrayList<>(this.taxpayers.values());
// 按总消费降序排序
taxpayerList.sort((t1, t2) -> {
    BigDecimal s1 = totalSpendingMap.getOrDefault(t1.getTIN(), BigDecimal.ZERO);
    BigDecimal s2 = totalSpendingMap.getOrDefault(t2.getTIN(), BigDecimal.ZERO);
    return s2.compareTo(s1);
});
// 取前10个,注意处理纳税人不足10个的情况
List<Taxpayer> topTenList = taxpayerList.subList(0, Math.min(10, taxpayerList.size()));
// 如果必须返回TreeSet,转成TreeSet即可
TreeSet<Taxpayer> topTen = new TreeSet<>(spendingComparator);
topTen.addAll(topTenList);
整合后的完整方法

把上面的逻辑整合到你的getTenTaxpayers方法里,最终代码是这样的:

public TreeSet<Taxpayer> getTenTaxpayers() {
    // 1. 预计算每个TIN的总消费金额
    Map<String, BigDecimal> totalSpendingMap = new HashMap<>();
    for (Invoice invoice : this.invoices.values()) {
        String tin = invoice.getTIN();
        BigDecimal amount = invoice.getAmount();
        totalSpendingMap.merge(tin, amount, BigDecimal::add);
    }

    // 2. 定义排序比较器
    Comparator<Taxpayer> spendingComparator = (t1, t2) -> {
        BigDecimal s1 = totalSpendingMap.getOrDefault(t1.getTIN(), BigDecimal.ZERO);
        BigDecimal s2 = totalSpendingMap.getOrDefault(t2.getTIN(), BigDecimal.ZERO);
        int spendingCompare = s2.compareTo(s1);
        if (spendingCompare != 0) {
            return spendingCompare;
        }
        return t1.getTIN().compareTo(t2.getTIN());
    };

    // 3. 筛选并维护前10个消费最高的纳税人
    TreeSet<Taxpayer> topTen = new TreeSet<>(spendingComparator);
    for (Taxpayer taxpayer : this.taxpayers.values()) {
        topTen.add(taxpayer);
        if (topTen.size() > 10) {
            topTen.pollLast();
        }
    }

    return topTen;
}
几个关键注意点
  • 效率优化:原来的嵌套遍历是O(N*M)的时间复杂度,预计算总消费的方式是O(N+M),当发票数量很大时差异特别明显。
  • 空值处理:用getOrDefault给无发票的纳税人设置0消费,避免空指针。
  • TreeSet去重:必须加入TIN的比较逻辑,否则总消费相同的纳税人会被TreeSet误判为重复元素而丢弃。

内容的提问来源于stack exchange,提问作者John Mulaney

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 04:14:36