如何用Java 8 StreamAPI优化实现带折扣的图书总价计算?
问题分析与优化方案
原代码的核心问题
你的代码存在两个关键问题:
- 多次遍历集合:前后三次调用
books.stream(),相当于遍历了三次图书列表,大数据量场景下效率偏低。 - 逻辑错误:
books.stream().skip(2)是跳过原集合的前2个元素,而非排序后的最贵两本。如果原列表不是按价格从高到低排列,剩余价格的计算会完全错误,直接导致最终总价出错。
关于“是否必须排序”的疑问
要找出最贵的两本图书,不一定需要对整个列表排序:
- 全排序的时间复杂度是O(n logn),但我们只需要前两大的值,完全可以通过一次遍历记录最大的两个价格,时间复杂度O(n),效率更高。
- 如果偏好Stream API,排序是最直观的实现方式,但要注意只排一次,避免重复操作。
优化实现方式
方式一:一次遍历完成所有计算(最优效率)
这种方式仅遍历一次图书列表,同时计算总价、记录最贵的两个价格,适合大数据量场景:
public int getTotalPrice(ArrayList<Book> books) { if (books.size() < 5) { return books.stream().mapToInt(Book::getPrice).sum(); } int total = 0; int firstMax = 0; int secondMax = 0; for (Book book : books) { int price = book.getPrice(); total += price; // 更新最贵的两个价格 if (price > firstMax) { secondMax = firstMax; firstMax = price; } else if (price > secondMax) { secondMax = price; } } if (total >= this.minimumPrice) { // 总价减去最贵两本的半价(等价于原价总和 - (firstMax + secondMax)/2) return total - (firstMax + secondMax) / 2; } return total; }
方式二:基于Stream的优化(更简洁,避免多次遍历)
如果偏好Stream API,可以只排序一次后拆分处理,避免重复遍历:
public int getTotalPrice(ArrayList<Book> books) { int size = books.size(); if (size < 5) { return books.stream().mapToInt(Book::getPrice).sum(); } int total = books.stream().mapToInt(Book::getPrice).sum(); if (total < this.minimumPrice) { return total; } // 仅排序一次,获取降序排列的价格列表 List<Integer> sortedPrices = books.stream() .mapToInt(Book::getPrice) .boxed() .sorted(Collections.reverseOrder()) .collect(Collectors.toList()); // 计算折扣后总价:前两本半价 + 剩余图书原价 int discountedSum = (int) (sortedPrices.get(0) * 0.5 + sortedPrices.get(1) * 0.5); int restSum = sortedPrices.stream().skip(2).mapToInt(Integer::intValue).sum(); return discountedSum + restSum; }
补充说明
- 方式一效率更高,无排序开销;方式二更简洁,适合数据量不大的场景。
- 注意价格为int类型时,半价计算会丢失小数部分(如3元半价转int为1元),这和你原代码逻辑一致,若需保留精度可改用double类型返回。
内容的提问来源于stack exchange,提问作者satanmoo
相关产品推荐
相关产品推荐

