Java 8 Stream 最适合处理集合中的“筛选、转换、分组、聚合”。它不是为了炫语法,而是把一批常见循环写成更接近数据处理流程的表达。
先准备一个示例对象
public class Apple {
private Integer id;
private String name;
private BigDecimal money;
private Integer num;
}
示例数据:
List<Apple> apples = List.of(
new Apple(1, "苹果1", new BigDecimal("3.25"), 10),
new Apple(1, "苹果2", new BigDecimal("1.35"), 20),
new Apple(2, "香蕉", new BigDecimal("2.89"), 30),
new Apple(3, "荔枝", new BigDecimal("9.99"), 40)
);
按字段分组
按 id 分组:
Map<Integer, List<Apple>> groupById = apples.stream()
.collect(Collectors.groupingBy(Apple::getId));
适合把订单按用户、商品按分类、日志按状态聚合。
List 转 Map
如果 id 唯一,可以直接转:
Map<Integer, Apple> appleMap = apples.stream()
.collect(Collectors.toMap(Apple::getId, apple -> apple));
如果 key 可能重复,必须提供合并策略,否则会抛 Duplicate key:
Map<Integer, Apple> appleMap = apples.stream()
.collect(Collectors.toMap(
Apple::getId,
apple -> apple,
(first, second) -> first
));
这里保留第一个元素,也可以按业务改成保留最新、合并金额或抛业务异常。
过滤元素
查找名称为“香蕉”的元素:
List<Apple> result = apples.stream()
.filter(apple -> "香蕉".equals(apple.getName()))
.collect(Collectors.toList());
过滤条件里注意空指针,常量放前面是一个简单习惯。
BigDecimal 求和
金额求和不要转 double,直接用 BigDecimal::add:
BigDecimal total = apples.stream()
.map(Apple::getMoney)
.reduce(BigDecimal.ZERO, BigDecimal::add);
如果金额可能为 null,要先过滤或补零。
最大值和最小值
Optional<Apple> max = apples.stream()
.collect(Collectors.maxBy(Comparator.comparing(Apple::getNum)));
Optional<Apple> min = apples.stream()
.collect(Collectors.minBy(Comparator.comparing(Apple::getNum)));
结果是 Optional,因为集合可能为空。
按字段去重
按 id 去重可以用 TreeSet:
List<Apple> unique = apples.stream()
.collect(Collectors.collectingAndThen(
Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Apple::getId))),
ArrayList::new
));
这种方式适合小集合。大集合或复杂业务中,更推荐用 Map 合并策略表达“重复时怎么处理”。
实用结论
Stream 写法的重点是让数据流向清楚:先 stream(),再 filter / map,最后 collect 或 reduce。如果逻辑很复杂、需要大量副作用或异常处理,普通循环反而更清楚。不要为了函数式而函数式,能让代码更短、更稳、更易读才值得用。
正文完




