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

如何在Java Stream中单次遍历获取某字段值最小的所有元素

单次遍历找出字段值最小的所有记录

针对你的需求,确实可以通过单次遍历实现,无需两次遍历或构建全部分组Map,下面提供两种优雅的Java实现方式:

方式一:自定义Collector(推荐,可读性高)

自定义Collector可以在遍历过程中维护当前的最小loanAmount以及对应的Person列表,逻辑清晰且支持并行流:

1. 定义辅助容器类

这个类负责处理单个元素、合并并行流的中间结果,并最终返回结果列表:

class MinLoanHolder {
    private BigDecimal minAmount;
    private List<Person> persons;

    private MinLoanHolder() {
        this.minAmount = null;
        this.persons = new ArrayList<>();
    }

    // 处理单个Person对象
    void accept(Person person) {
        BigDecimal currentLoan = person.loanAmount();
        if (minAmount == null) {
            // 第一个元素,初始化最小金额和列表
            minAmount = currentLoan;
            persons.add(person);
        } else {
            int compareResult = currentLoan.compareTo(minAmount);
            if (compareResult < 0) {
                // 找到更小的金额,重置列表
                minAmount = currentLoan;
                persons.clear();
                persons.add(person);
            } else if (compareResult == 0) {
                // 金额相等,添加到列表
                persons.add(person);
            }
            // 金额更大,直接忽略
        }
    }

    // 合并两个Holder(并行流场景下需要)
    MinLoanHolder combine(MinLoanHolder other) {
        if (this.minAmount == null) return other;
        if (other.minAmount == null) return this;

        int compareResult = this.minAmount.compareTo(other.minAmount);
        if (compareResult < 0) {
            return this;
        } else if (compareResult > 0) {
            return other;
        } else {
            // 金额相同,合并两个列表
            this.persons.addAll(other.persons);
            return this;
        }
    }

    // 返回最终结果
    List<Person> getResult() {
        return persons;
    }
}

2. 创建并使用自定义Collector

// 定义Collector
Collector<Person, ?, List<Person>> minLoanCollector = Collector.of(
        MinLoanHolder::new,
        MinLoanHolder::accept,
        MinLoanHolder::combine,
        MinLoanHolder::getResult
);

// 假设persons是从API转换得到的Person流
List<Person> minLoanPersons = persons.stream()
        .collect(minLoanCollector);

方式二:使用Stream.reduce()(无需额外类)

如果不想定义辅助类,可以用reduce方法,通过数组临时存储当前的最小金额和对应列表:

// 假设persons是从API转换得到的Person流
List<Person> minLoanPersons = persons.stream()
        .reduce(
                // 初始值:[当前最小金额, 对应Person列表]
                new Object[]{null, new ArrayList<Person>()},
                // 累积逻辑:处理每个Person并更新状态
                (acc, person) -> {
                    BigDecimal currentLoan = person.loanAmount();
                    BigDecimal minAmount = (BigDecimal) acc[0];
                    List<Person> list = (List<Person>) acc[1];

                    if (minAmount == null) {
                        acc[0] = currentLoan;
                        list.add(person);
                    } else {
                        int compareResult = currentLoan.compareTo(minAmount);
                        if (compareResult < 0) {
                            acc[0] = currentLoan;
                            list.clear();
                            list.add(person);
                        } else if (compareResult == 0) {
                            list.add(person);
                        }
                    }
                    return acc;
                },
                // 合并逻辑:并行流时合并两个状态
                (acc1, acc2) -> {
                    BigDecimal min1 = (BigDecimal) acc1[0];
                    BigDecimal min2 = (BigDecimal) acc2[0];
                    List<Person> list1 = (List<Person>) acc1[1];
                    List<Person> list2 = (List<Person>) acc2[1];

                    if (min1 == null) return acc2;
                    if (min2 == null) return acc1;

                    int compareResult = min1.compareTo(min2);
                    if (compareResult < 0) {
                        return acc1;
                    } else if (compareResult > 0) {
                        return acc2;
                    } else {
                        list1.addAll(list2);
                        return acc1;
                    }
                }
        )[1]; // 取出列表部分

注意事项

  • 比较BigDecimal必须用compareTo方法,不要用equals,因为equals会同时比较值和精度(比如1.0和1.00会被判定为不相等),而业务上通常认为这两个金额是相同的。
  • 两种方式都支持并行流,且全程只存储当前最小金额对应的记录,内存效率远高于groupingBy方案。

内容的提问来源于stack exchange,提问作者Hrishikesh Joshi

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 12:31:24