Java Stream 流基本概念和使用方式详解
作者:MadeInSQL
Stream是Java 8 引入的全新API,它基于函数式编程思想,为集合操作提供了一种声明式的处理方式,本文给大家介绍Java Stream流基本概念和核心特性,感兴趣的朋友一起看看吧
一、Stream 基础概念与核心特性
1.1 什么是 Stream 流
Stream 是 Java 8 引入的全新API,它基于函数式编程思想,为集合操作提供了一种声明式的处理方式。与传统的命令式集合操作不同,Stream API 使得数据处理更加高效且易于并行化。
核心特点详解:
- 惰性求值(Lazy Evaluation):
- 中间操作(如 filter、map)不会立即执行
- 只有遇到终止操作(如 collect、forEach)时才会触发计算
- 示例:
list.stream().filter(x -> x > 10).count()
中,filter 操作在 count 被调用时才执行
- 不可复用(Single Use):
- 每个 Stream 管道(pipeline)只能被消费一次
- 重复使用会抛出 IllegalStateException
- 解决方案:每次需要时重新创建 Stream
- 内部迭代(Internal Iteration):
- 不需要显式编写 for/while 循环
- 迭代过程由 Stream API 内部处理
- 对比:传统 for 循环是外部迭代
- 函数式风格(Functional Style):
- 支持 Lambda 表达式(如 x -> x*2)
- 支持方法引用(如 String::length)
- 无副作用:理想情况下不修改外部状态
典型使用场景:
- 数据筛选过滤(filter)
- 数据转换映射(map)
- 聚合计算(reduce、collect)
- 并行数据处理(parallelStream)
示例代码:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie"); // 传统方式 for(String name : names) { if(name.startsWith("A")) { System.out.println(name.toUpperCase()); } } // Stream方式 names.stream() .filter(name -> name.startsWith("A")) .map(String::toUpperCase) .forEach(System.out::println);
1.2 Stream 与集合的区别
特性 | 集合(Collection) | Stream(流) |
---|---|---|
存储 | 存储实际数据 | 不存储数据 |
操作方式 | 外部迭代(foreach循环) | 内部迭代 |
数据处理 | 立即执行 | 延迟执行 |
可重用性 | 可多次遍历 | 只能遍历一次 |
并行能力 | 需要手动实现 | 内置并行支持 |
二、Stream 创建方式详解
2.1 从集合创建
// 从List创建 List<String> list = Arrays.asList("a", "b", "c"); Stream<String> stream1 = list.stream(); // 从Set创建 Set<Integer> set = new HashSet<>(Arrays.asList(1, 2, 3)); Stream<Integer> stream2 = set.stream();
2.2 从数组创建
String[] array = {"a", "b", "c"}; Stream<String> stream3 = Arrays.stream(array); // 指定范围 Stream<String> stream4 = Arrays.stream(array, 1, 3); // "b", "c"
2.3 使用静态工厂方法
// 使用Stream.of() Stream<String> stream5 = Stream.of("a", "b", "c"); // 创建空流 Stream<String> emptyStream = Stream.empty(); // 创建无限流 Stream<Integer> infiniteStream1 = Stream.iterate(0, n -> n + 2); // 0,2,4,6... Stream<Double> infiniteStream2 = Stream.generate(Math::random); // 随机数流
2.4 其他创建方式
// 从文件创建 try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) { lines.forEach(System.out::println); } // 从字符串创建 IntStream charStream = "Hello".chars();
三、Stream 核心操作全解析
3.1 中间操作(Intermediate Operations)
过滤操作
// filter() - 筛选偶数 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens = numbers.stream() .filter(n -> n % 2 == 0) .collect(Collectors.toList()); // [2, 4] // distinct() - 去重 List<String> names = Arrays.asList("John", "John", "Mary"); List<String> uniqueNames = names.stream() .distinct() .collect(Collectors.toList()); // ["John", "Mary"]
映射操作
// map() - 字符串转大写 List<String> upperNames = names.stream() .map(String::toUpperCase) .collect(Collectors.toList()); // flatMap() - 扁平化处理 List<List<String>> nestedList = Arrays.asList( Arrays.asList("a", "b"), Arrays.asList("c", "d") ); List<String> flatList = nestedList.stream() .flatMap(Collection::stream) .collect(Collectors.toList()); // ["a", "b", "c", "d"]
限制与跳过
// limit() - 限制数量 List<Integer> limited = numbers.stream() .limit(3) .collect(Collectors.toList()); // [1, 2, 3] // skip() - 跳过元素 List<Integer> skipped = numbers.stream() .skip(2) .collect(Collectors.toList()); // [3, 4, 5]
排序操作
// sorted() - 自然排序 List<String> sortedNames = names.stream() .sorted() .collect(Collectors.toList()); // sorted(Comparator) - 自定义排序 List<Person> people = Arrays.asList( new Person("John", 25), new Person("Mary", 20) ); List<Person> sortedByAge = people.stream() .sorted(Comparator.comparing(Person::getAge)) .collect(Collectors.toList());
3.2 终端操作(Terminal Operations)
匹配与查找
// anyMatch() - 是否有元素匹配 boolean hasEven = numbers.stream() .anyMatch(n -> n % 2 == 0); // true // findFirst() - 获取第一个元素 Optional<Integer> first = numbers.stream() .findFirst(); // Optional[1]
聚合操作
// count() - 计数 long count = numbers.stream().count(); // 5 // max()/min() - 最大最小值 Optional<Integer> max = numbers.stream() .max(Integer::compare); // Optional[5] // reduce() - 归约操作 Optional<Integer> sum = numbers.stream() .reduce(Integer::sum); // Optional[15]
收集操作
// collect() - 转换为集合 Set<Integer> numberSet = numbers.stream() .collect(Collectors.toSet()); // joining() - 字符串连接 String joined = names.stream() .collect(Collectors.joining(", ")); // "John, John, Mary" // groupingBy() - 分组 Map<Integer, List<Person>> peopleByAge = people.stream() .collect(Collectors.groupingBy(Person::getAge));
遍历操作
// forEach() - 遍历消费 numbers.stream().forEach(System.out::println); // forEachOrdered() - 保证顺序 numbers.parallelStream().forEachOrdered(System.out::println);
四、并行流与性能优化
4.1 并行流基础
// 创建并行流 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); // 方式1:从集合创建 Stream<Integer> parallelStream1 = numbers.parallelStream(); // 方式2:将顺序流转并行 Stream<Integer> parallelStream2 = numbers.stream().parallel(); // 示例:并行处理 long count = numbers.parallelStream() .filter(n -> n % 2 == 0) .count();
4.2 性能考量
适合使用并行流的场景:
- 数据量较大(通常超过1万元素)
- 单个元素处理耗时较长
- 操作是无状态的(不依赖之前处理的结果)
- 操作不要求顺序
注意事项:
- 并行化本身有开销,小数据集可能更慢
- 确保线程安全,避免共享可变状态
- 考虑使用
unordered()
去除顺序约束提升性能
4.3 自定义线程池
// 使用自定义ForkJoinPool ForkJoinPool customPool = new ForkJoinPool(4); long result = customPool.submit(() -> numbers.parallelStream() .filter(n -> n % 2 == 0) .count() ).get();
五、实战应用案例
5.1 数据处理案例
// 案例1:统计单词频率 List<String> words = Arrays.asList("hello", "world", "hello", "java"); Map<String, Long> wordCounts = words.stream() .collect(Collectors.groupingBy( Function.identity(), Collectors.counting() )); // 案例2:多级分组 List<Employee> employees = ...; Map<String, Map<String, List<Employee>>> byDeptAndCity = employees.stream() .collect(Collectors.groupingBy(Employee::getDepartment, Collectors.groupingBy(Employee::getCity)));
5.2 文件处理案例
// 读取文件并处理 try (Stream<String> lines = Files.lines(Paths.get("data.txt"))) { List<String> filteredLines = lines .filter(line -> !line.startsWith("#")) // 过滤注释行 .map(String::trim) // 去除空格 .collect(Collectors.toList()); } // 查找文件中最长的行 Optional<String> longest = Files.lines(Paths.get("data.txt")) .max(Comparator.comparing(String::length));
5.3 数据库查询结果处理
// 使用Stream处理JDBC结果 List<Person> persons = new ArrayList<>(); try (Connection conn = DriverManager.getConnection(url, user, pass); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM persons")) { Stream<ResultSet> stream = StreamSupport.stream( new Spliterators.AbstractSpliterator<ResultSet>( Long.MAX_VALUE, Spliterator.ORDERED) { public boolean tryAdvance(Consumer<? super ResultSet> action) { try { if (!rs.next()) return false; action.accept(rs); return true; } catch (SQLException e) { throw new RuntimeException(e); } } }, false); persons = stream.map(rs -> new Person(rs.getString("name"), rs.getInt("age"))) .collect(Collectors.toList()); }
六、常见问题与最佳实践
6.1 常见陷阱
重复使用Stream:
Stream<String> stream = Stream.of("a", "b", "c"); stream.forEach(System.out::println); stream.forEach(System.out::println); // 抛出IllegalStateException
修改源集合:
List<String> list = new ArrayList<>(Arrays.asList("a", "b", "c")); Stream<String> stream = list.stream(); list.add("d"); // 可能导致ConcurrentModificationException
无限流未限制:
Stream.iterate(0, i -> i + 1) .forEach(System.out::println); // 无限循环
6.2 性能优化建议
优先使用基本类型流:
// 使用IntStream代替Stream<Integer> IntStream.range(0, 100).sum();
短路操作优化:
// 使用anyMatch代替filter+findFirst boolean hasNegative = numbers.stream().anyMatch(n -> n < 0);
合并中间操作:
// 合并多个filter list.stream().filter(s -> s != null && s.length() > 3);
6.3 调试技巧
使用peek()调试:
List<String> result = list.stream() .peek(System.out::println) // 调试输出 .filter(s -> s.length() > 3) .peek(System.out::println) // 调试输出 .collect(Collectors.toList());
转换为并行流查找问题:
list.stream() .parallel() .map(s -> {throw new RuntimeException();}) // 更容易暴露线程安全问题 .collect(Collectors.toList());
七、高级特性与未来展望
7.1 Java 9+ 增强特性
// takeWhile/dropWhile (Java 9+) List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 3, 2, 1); List<Integer> taken = numbers.stream() .takeWhile(n -> n < 4) // [1, 2, 3] .collect(Collectors.toList()); // ofNullable (Java 9+) Stream<String> stream = Stream.ofNullable(System.getProperty("key")); // iterate增强 (Java 9+) Stream.iterate(0, n -> n < 10, n -> n + 1) // 类似for循环 .forEach(System.out::println);
7.2 响应式编程结合
// 与RxJava/Reactor结合示例 Flux.fromStream(Stream.generate(() -> "data")) .delayElements(Duration.ofMillis(100)) .take(10) .subscribe(System.out::println);
7.3 未来发展方向
- 值类型流:对基本类型更高效的支持
- 模式匹配:增强流处理表达能力
- 更强大的收集器:提供更多内置收集操作
到此这篇关于Java Stream 流详解的文章就介绍到这了,更多相关Java Stream 流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
到此这篇关于Java Stream 流基本概念和使用方式详解的文章就介绍到这了,更多相关Java Stream 流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!