在Java 8中,Stream API的引入彻底改变了我们处理集合数据的方式。Stream提供了一种声明式、函数式的编程模型,使得集合操作更加简洁、高效且易于并行化。本文将深入探讨Java Stream的核心概念、常用操作以及实际应用场景。
Stream不是数据结构,而是对数据源(如集合、数组、I/O资源等)的一种高级抽象,支持顺序和并行聚合操作。主要特点包括:
// 1. 从集合创建
List list = Arrays.asList("a", "b", "c");
Stream stream1 = list.stream();
// 2. 从数组创建
String[] array = {"a", "b", "c"};
Stream stream2 = Arrays.stream(array);
// 3. 使用Stream.of()
Stream stream3 = Stream.of("a", "b", "c");
// 4. 生成无限流
Stream infiniteStream = Stream.iterate(0, n -> n + 1);
Stream randomStream = Stream.generate(Math::random);
// 5. 基本类型流
IntStream intStream = IntStream.range(1, 10);
LongStream longStream = LongStream.rangeClosed(1, 10);
Stream操作分为中间操作(Intermediate Operations)和终端操作(Terminal Operations)。
过滤操作(过滤出长度大于3的字符串)
// 过滤出长度大于3的字符串
List filtered = list.stream()
.filter(s -> s.length() > 3)
.collect(Collectors.toList());
映射操作(将字符串转换为大写)
// 将字符串转换为大写
List upperCase = list.stream()
.map(String::toUpperCase)
.collect(Collectors.toList());
// 扁平化处理
List words = Arrays.asList("Hello", "World");
List letters = words.stream()
.flatMap(word -> Arrays.stream(word.split("")))
.collect(Collectors.toList());
输出:
//对每个单词执行 split("") 拆分为字母数组:
//"Hello" → ["H", "e", "l", "l", "o"]
//"World" → ["W", "o", "r", "l", "d"]
//Arrays.stream() 将每个数组转为Stream
//flatMap 将所有Stream合并为一个Stream
排序操作
// 自然排序
List sorted = list.stream()
.sorted()
.collect(Collectors.toList());
如果在实际开发中,推荐使用Comparator
的工具(排序)方法,代码更清晰:
// 按长度升序
.sorted(Comparator.comparingInt(String::length))
// 按长度降序
.sorted(Comparator.comparingInt(String::length).reversed())
// 先按长度,长度相同再按字母顺序
.sorted(Comparator.comparingInt(String::length)
.thenComparing(Comparator.naturalOrder()))
去重操作
List distinct = list.stream()
.distinct()
.collect(Collectors.toList());
限制/跳过元素
// 取前5个
List limited = list.stream()
.limit(5)
.collect(Collectors.toList());
// 跳过前2个
List skipped = list.stream()
.skip(2)
.collect(Collectors.toList());
1.遍历操作
list.stream().forEach(System.out::println);
2.聚合操作
// 计数
long count = list.stream().count();
// 最大值/最小值
Optional max = list.stream().max(Comparator.naturalOrder());
Optional min = list.stream().min(Comparator.naturalOrder());
// 匹配
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("A"));
boolean allMatch = list.stream().allMatch(s -> s.length() > 3);
boolean noneMatch = list.stream().noneMatch(s -> s.isEmpty());
3.归约操作
// 求和
int sum = IntStream.of(1, 2, 3).reduce(0, (a, b) -> a + b);
// 字符串连接
String concatenated = list.stream().reduce("", String::concat);
4.收集操作
// 转换为List
List collectedList = list.stream().collect(Collectors.toList());
// 转换为Set
Set collectedSet = list.stream().collect(Collectors.toSet());
// 转换为Map
Map map = list.stream()
.collect(Collectors.toMap(Function.identity(), String::length));
// 分组
Map> grouped = list.stream()
.collect(Collectors.groupingBy(String::length));
// 分区
Map> partitioned = list.stream()
.collect(Collectors.partitioningBy(s -> s.length() > 3));
// 连接字符串
String joined = list.stream().collect(Collectors.joining(", "));
// 统计汇总
IntSummaryStatistics stats = list.stream()
.collect(Collectors.summarizingInt(String::length));
在银行系统中,每日交易流水的汇总与统计 是最常见的高频 Stream 应用场景之一。例如,银行需要实时计算客户的交易总额、分类统计交易类型、筛选异常交易等。
示例代码:统计客户当日交易总额与分类汇总
// 假设 Transaction 包含 amount(金额)、type(交易类型)、customerId(客户ID)等字段
List dailyTransactions = transactionRepository.findByDate(LocalDate.now());
// 1. 计算当日总交易额(使用 sum)
double totalAmount = dailyTransactions.stream()
.mapToDouble(Transaction::getAmount) // 转为 double 流
.sum(); // 终端操作:求和
// 2. 按交易类型分组统计(如 DEPOSIT, WITHDRAWAL, TRANSFER)
Map sumByType = dailyTransactions.stream()
.collect(Collectors.groupingBy(
Transaction::getType, // 按交易类型分组
Collectors.summingDouble(Transaction::getAmount) // 对每组金额求和
));
// 3. 筛选大额交易(超过 50 万)并触发风控
List largeTransactions = dailyTransactions.stream()
.filter(t -> t.getAmount() > 500_000) // 过滤条件
.peek(t -> riskControlService.check(t)) // 对每笔大额交易触发风控检查
.collect(Collectors.toList()); // 收集结果
// 4. 找出交易次数最多的客户(用于重点客户分析)
Optional topCustomerId = dailyTransactions.stream()
.collect(Collectors.groupingBy(
Transaction::getCustomerId,
Collectors.counting() // 统计每个客户的交易次数
))
.entrySet().stream()
.max(Map.Entry.comparingByValue()) // 找出交易次数最多的客户
.map(Map.Entry::getKey); // 返回客户ID
注:对于上面代码中的第三点,如果可能,优先使用 forEach
替代 peek
,让Stream操作更纯粹。
// 先过滤,再显式遍历(避免Stream副作用)
List largeTransactions = dailyTransactions.stream()
.filter(t -> t.getAmount() > 500_000)
.collect(Collectors.toList());
largeTransactions.forEach(riskControlService::check); // 明确表示这是终端操作
作者留言:
对于Stream API的用法,一篇博文无法将其所有的知识点一一列举,此篇博文只是给大家一个大致了解,希望大家可以在具体的实际项目中遇到问题解决问题,从而提升Stream流的熟练度!!!