Java Stream API的二度深入

前言
为什么会写这样一篇文章呢?
1.面试的时候,一位前辈对我这方面有过一次提问,我随口回答,前辈很信任我,以此文致敬前辈!
2.去回顾,去扎实,对得起前辈的信任!
3.Stream API 用起来很方便,所以很重要,所以还得学!
Stream API的三个阶段
在Java中,Stream 是Java 8引入的一个新概念,用于处理集合(Collections)数据的一种抽象。Java的Stream API 提供了一种声明式的方式来操作数据集合,可以用更简洁、可读性更强的代码来进行集合的操作。
Java Stream API的操作可以分为三个阶段:
-
创建流(Creation of Stream): 这个阶段涉及到从不同的数据源创建流,可以是集合、数组、I/O通道等。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> stream = numbers.stream(); -
中间操作(Intermediate Operations): 这个阶段包括对流的转换操作,可以对流进行过滤、映射、排序等操作。这些操作并不会改变原始数据源,而是返回一个新的流。
Stream<Integer> filteredStream = stream.filter(x -> x > 2); -
终端操作(Terminal Operations): 这个阶段是对流进行最终操作,触发流的遍历,可以产生一个结果或者副作用。终端操作是流的最后一个操作,执行后流将不可再用。
long count = filteredStream.count();
这三个阶段的设计使得可以通过链式调用的方式组合多个操作,从而编写更为清晰和简洁的代码。这种方式也有助于提高代码的可读性和可维护性。
当然,这里只是对于Stream API三个阶段的概述,只是告诉大家,简单分为三个阶段,至于三个阶段里面有哪些主要的方法,我们在下文进行详细叙述,这里我们点到为止!现在,大家心里面就应该有这么一个蓝图,或者是基本框架,知道我们接下来将会沿着那个几个方向展开叙述!
创建Stream流
在Java中,你可以使用多种方式来创建Stream流。
-
从集合创建:
使用集合类的
stream()或parallelStream()方法可以创建对应的流。例如:List<String> list = Arrays.asList("apple", "banana", "orange"); Stream<String> streamFromList = list.stream(); -
从数组创建:
使用
Arrays.stream()方法可以从数组中创建流:String[] array = {"apple", "banana", "orange"}; Stream<String> streamFromArray = Arrays.stream(array); -
通过Stream的静态方法创建:
Stream类提供了静态方法of(),可以传入一系列元素来创建流:Stream<String> stream = Stream.of("apple", "banana", "orange"); -
使用Stream的
generate和iterate方法:Stream类还提供了generate和iterate方法,用于生成无限流:// 生成包含随机整数的无限流 Stream<Integer> infiniteStream = Stream.generate(() -> (int) (Math.random() * 100)); // 从指定的起始值开始,按照某个规则生成无限流 Stream<Integer> sequentialStream = Stream.iterate(1, n -> n + 1); -
通过文件生成流:
java.nio.file.Files类提供了静态方法lines(),可以用来读取文件内容并生成流:Path path = Paths.get("example.txt"); Stream<String> fileLines = Files.lines(path); -
使用正则表达式生成流:
Pattern类的splitAsStream方法可以根据正则表达式将字符串分割成流:String text = "apple,orange,banana"; Stream<String> textStream = Pattern.compile(",").splitAsStream(text);
Stream API中间操作
Stream API 提供了许多中间操作,用于对流进行转换、筛选和处理。
-
filter:用于筛选元素,根据指定的条件保留符合条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> filteredStream = numbers.stream().filter(x -> x > 2); -
map:对流中的每个元素应用指定的函数,并将结果映射为一个新的元素。
List<String> words = Arrays.asList("apple", "banana", "orange"); Stream<Integer> wordLengths = words.stream().map(String::length); -
flatMap:将流中的每个元素都转换为一个流,然后将这些流连接起来成为一个流。
List<List<Integer>> numbers = Arrays.asList( Arrays.asList(1, 2), Arrays.asList(3, 4), Arrays.asList(5, 6) ); Stream<Integer> flatStream = numbers.stream().flatMap(List::stream); -
distinct:去除流中的重复元素。
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 4, 4, 5); Stream<Integer> distinctNumbers = numbers.stream().distinct(); -
sorted:对流中的元素进行排序。
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6); Stream<Integer> sortedNumbers = numbers.stream().sorted(); -
peek:对流中的每个元素执行操作,主要用于调试和观察流中的元素。
List<String> words = Arrays.asList("apple", "banana", "orange"); Stream<String> peekStream = words.stream().peek(System.out::println); -
limit和skip:limit用于截断流,保留指定数量的元素,而skip则用于跳过指定数量的元素。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> limitedStream = numbers.stream().limit(3); Stream<Integer> skippedStream = numbers.stream().skip(2); -
takeWhile和dropWhile(Java 9+):takeWhile返回元素满足指定条件的最长前缀,dropWhile返回删除满足指定条件的最长前缀后的流。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> takenWhileStream = numbers.stream().takeWhile(x -> x < 4); Stream<Integer> droppedWhileStream = numbers.stream().dropWhile(x -> x < 4);
Stream API终端操作
Stream API 的终端操作用于触发对流的最终操作,产生结果或者引起副作用。
-
forEach:对流中的每个元素执行指定的操作。
List<String> words = Arrays.asList("apple", "banana", "orange"); words.stream().forEach(System.out::println); -
toArray:将流中的元素转换为数组。
List<String> words = Arrays.asList("apple", "banana", "orange"); String[] wordArray = words.stream().toArray(String[]::new); -
reduce:对流中的元素进行归约操作,可以用于求和、求最大值、最小值等。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Optional<Integer> sum = numbers.stream().reduce(Integer::sum); -
collect:将流中的元素收集到一个集合中,例如 List、Set 或 Map。
List<String> words = Arrays.asList("apple", "banana", "orange"); List<String> collectedWords = words.stream().collect(Collectors.toList()); -
count:返回流中的元素数量。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); long count = numbers.stream().count(); -
anyMatch、allMatch和noneMatch:用于检查流中是否存在满足指定条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); boolean anyGreaterThanThree = numbers.stream().anyMatch(x -> x > 3); boolean allGreaterThanTwo = numbers.stream().allMatch(x -> x > 2); boolean noneGreaterThanFive = numbers.stream().noneMatch(x -> x > 5); -
findAny和findFirst:返回流中的任意一个元素或者第一个元素。
List<String> words = Arrays.asList("apple", "banana", "orange"); Optional<String> anyWord = words.stream().findAny(); Optional<String> firstWord = words.stream().findFirst(); -
min和max:返回流中的最小值或最大值。
List<Integer> numbers = Arrays.asList(3, 1, 4, 1, 5, 9, 2, 6); Optional<Integer> minNumber = numbers.stream().min(Integer::compare); Optional<Integer> maxNumber = numbers.stream().max(Integer::compare);
注意事项
使用Stream API时,有一些需要注意的重要事项,以确保正确、高效地利用这一功能:
-
只能使用一次: 一个
Stream实例只能被消费(执行终端操作)一次。如果你尝试对已经使用过的流进行其他终端操作,会抛出IllegalStateException异常。如果需要再次操作,可以重新创建一个新的流。List<String> words = Arrays.asList("apple", "banana", "orange"); Stream<String> wordStream = words.stream(); // 正确的做法 long count = wordStream.count(); // 错误的做法,会抛出IllegalStateException long anotherCount = wordStream.count(); -
及早退出: 在处理大量数据时,及早退出可以提高性能。使用
anyMatch()、findFirst()等终端操作时,一旦找到符合条件的元素,就会立即返回,不再继续处理后续元素。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); boolean anyGreaterThanThree = numbers.stream().anyMatch(x -> { System.out.println("Checking: " + x); return x > 3; }); -
并行流的谨慎使用: Stream API 提供了并行流的支持,可以通过
parallel()方法将顺序流转换为并行流。但并不是所有的场景都适合使用并行流,因为在某些情况下,并行流可能会导致性能下降,甚至出现并发问题。在并行流的使用上需要注意线程安全等问题。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); long count = numbers.parallelStream().filter(x -> x > 2).count(); -
避免可变状态: 在使用
map()、flatMap()等操作时,避免修改流中的元素或者使用可变状态。这有助于确保流操作的无状态性,避免副作用。// 避免修改流中的元素 List<String> words = Arrays.asList("apple", "banana", "orange"); words.stream().map(String::toUpperCase).forEach(System.out::println); // 避免使用可变状态 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); int sum = numbers.stream().reduce(0, Integer::sum); -
了解操作的性能特性: 不同的操作对性能的影响是不同的。例如,
limit()对顺序流的性能影响较小,但对并行流的性能影响较大。在选择操作时,了解其性能特性对于优化代码是有帮助的。List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5); Stream<Integer> limitedStream = numbers.stream().limit(3); // 对顺序流影响较小,但对并行流影响较大 -
使用适当的数据结构: 在创建流时,选择适当的数据结构能够影响流操作的性能。例如,
ArrayList在顺序访问时性能较好,而LinkedList在随机访问时性能较好。List<String> words = new ArrayList<>(Arrays.asList("apple", "banana", "orange")); Stream<String> wordStream = words.stream();
总体而言,了解Stream API的使用原则,结合具体的业务场景和性能需求,能够更好地利用Stream API完成任务。注意流的延迟计算特性,避免副作用,可以使代码更加清晰、可读,并提高代码的可维护性。




![第十四章[面向对象]:14.9:定制类](https://img-blog.csdnimg.cn/img_convert/24185a6f8f5567accc7da3ae8eded796.jpeg)







![给定长度为n(n<=20)的数组a,-20<=ai<=20, 每次操作选择i,j,使a[i] += a[j], 在31次操作内使a不递减,输出每次操作的i,j](https://img-blog.csdnimg.cn/direct/b7958c1f23bb46e79983ccc4e11940f6.png)






![[HTML]Web前端开发技术28(HTML5、CSS3、JavaScript )JavaScript基础——喵喵画网页](https://img-blog.csdnimg.cn/direct/1b9dece61d314044a355d452d141db77.png)