一、概述
Stream流思想是一种函数式编程的思想,它提供了一种流式处理集合数据的方式。它的核心思想是将操作集合的过程抽象为一系列的中间操作和终止操作,并通过方法链的方式来组合这些操作。
Stream是Java 8引入的一个用于处理集合数据的API。它提供了一种声明式的编程方式,可以对集合数据进行各种操作,如过滤、映射、排序、聚合等。
二、Stream的主要特点包括:
- 声明式编程:Stream API提供了丰富的方法来描述对集合数据的操作,可以更直观地表达数据处理的意图,而不需要编写繁琐的循环和条件语句。
- 惰性求值:Stream的操作通常是惰性求值的,也就是说,在调用终止操作之前,中间操作不会立即执行,而是等到需要结果时才进行计算,这样可以避免不必要的计算开销。
- 并行处理:Stream可以通过并行处理来提高处理大量数据的效率,可以在某些情况下自动利用多核处理器的优势,并行执行操作。
- 函数式编程:Stream API使用函数式接口和Lambda表达式,可以以更简洁、灵活的方式编写代码。
三、使用Stream的基本步骤如下:
1.准备数据源:首先需要有一个数据源,可以是集合(List、Set、Map等),也可以是数组等。
2.创建Stream对象:通过数据源创建Stream对象,可以使用Stream接口的静态方法或Collection接口的实例方法来创建一个Stream。
①通过集合获取Stream流:可以使用stream()方法获取List、Set、Map等集合的Stream流。
List<String> list = Arrays.asList("apple", "banana", "cherry");
Stream<String> stream = list.stream();
②通过数组获取Stream流:可以使用Arrays.stream()方法获取数组的Stream流。
int[] numbers = {1, 2, 3, 4, 5};
IntStream stream = Arrays.stream(numbers);
③通过值创建Stream流:可以使用Stream.of()方法创建一个包含指定元素的Stream流。
Stream<String> stream = Stream.of("apple", "banana", "cherry");
3.中间操作:对Stream进行一系列的中间操作,如过滤、映射、排序等。每个中间操作都会返回一个新的Stream,可以通过链式调用来组合不同的中间操作。
4.终止操作:最后对Stream进行终止操作,例如聚合、收集、迭代等,得到最终的结果。
四、中间方法和终结方法
1、中间方法:
是对Stream对象进行处理的过程,在中间操作之后可以继续执行其他中间操作或者执行终止操作。Stream API提供了很多中间操作方法,下面列举一些常用的中间操作方法:
①filter(Predicate<T> predicate): 过滤操作,保留符合条件的元素,去掉不符合条件的元素。
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println(evenNumbers); // 输出 [2, 4, 6, 8, 10]
②map(Function<T, R> mapper): 映射操作,对Stream中的每个元素应用给定的映射函数。
List<String> words = Arrays.asList("apple", "banana", "cherry");
List<Integer> wordLengths = words.stream()
.map(String::length)
.collect(Collectors.toList());
System.out.println(wordLengths); // 输出 [5, 6, 6]
③flatMap(Function<T, Stream<R>> mapper): 扁平化操作,将Stream中的每个元素转换为Stream,并将各个Stream合并成一个Stream。
List<List<Integer>> numbers = Arrays.asList(
Arrays.asList(1, 2),
Arrays.asList(3, 4),
Arrays.asList(5, 6)
);
List<Integer> flattenedNumbers = numbers.stream()
.flatMap(List::stream)
.collect(Collectors.toList());
System.out.println(flattenedNumbers); // 输出 [1, 2, 3, 4, 5, 6]
④distinct(): 去重操作,返回一个去重后的Stream。
List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 4, 4, 4);
List<Integer> distinctNumbers = numbers.stream()
.distinct()
.collect(Collectors.toList());
System.out.println(distinctNumbers); // 输出 [1, 2, 3, 4]
⑤sorted(): 排序操作,按照自然顺序排序,返回一个新的Stream。
List<Integer> numbers = Arrays.asList(3, 1, 4, 2, 5);
List<Integer> sortedNumbers = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println(sortedNumbers); // 输出 [1, 2, 3, 4, 5]
⑥limit()是用于限制流最大元素数量的方法。它返回一个新的流,该流包含原始流中的前n个元素(如果原始流中的元素数量小于n,则会包含所有元素)。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream();
stream.limit(5).forEach(System.out::println);
⑦skip()用于跳过流中的前N个元素,返回一个新的流。它可以用于忽略流中的前n个元素并获取剩余的元素。skip()方法可以这样使用:
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
Stream<Integer> stream = list.stream();
stream.skip(3).forEach(System.out::println);
⑧concat()是用于合并两个流的方法。这个方法接收两个流作为参数,并返回一个新的流,其中包含两个原始流的所有元素。具体使用方法如下:
List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Arrays.asList("d", "e", "f");
Stream<String> stream1 = list1.stream();
Stream<String> stream2 = list2.stream();
Stream<String> mergedStream = Stream.concat(stream1, stream2);
mergedStream.forEach(System.out::println);
每个中间操作返回的都是一个新的Stream对象,可以连续进行多次中间操作,并且不会对原有的数据源产生任何影响。通过组合不同的中间操作可以形成一个流水线,来实现复杂的数据处理逻辑。
2、终结方法:
在Java的Stream API中,终端操作(Terminal Operations)用于对流进行最终的处理,产生最终的结果或副作用。下面列举了一些常用的终端操作方法:
- forEach(Consumer<? super T> action):对流中的每个元素执行给定的操作。
- toArray():将流的元素收集到一个数组中。
- reduce(BinaryOperator<T> accumulator):将流的元素按照指定的累加器函数进行累计操作,返回一个Optional对象。
- collect(Collector<? super T, A, R> collector):将流的元素根据收集器进行汇总操作,并返回最终的结果。
- count():返回流中的元素数量。
- min(Comparator<? super T> comparator) / max(Comparator<? super T> comparator):根据比较器选择出流中的最小或最大元素。
- anyMatch(Predicate<? super T> predicate) / allMatch(Predicate<? super T> predicate) / noneMatch(Predicate<? super T> predicate):分别判断流中是否存在满足条件的元素、是否所有元素都满足条件、是否没有元素满足条件,并返回布尔值。
- findFirst() / findAny():返回第一个或任意一个流中的元素。
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
// 使用终结方法forEach对每个元素执行操作
list.stream().forEach(System.out::println);
// 使用toArray将流的元素收集到数组中
Integer[] array = list.stream().toArray(Integer[]::new);
System.out.println(Arrays.toString(array));
// 使用reduce将流的元素进行累加操作
Optional<Integer> sum = list.stream().reduce(Integer::sum);
System.out.println("Sum: " + sum.orElse(0));
// 使用collect将流的元素收集为一个字符串
String result = list.stream().map(Object::toString).collect(Collectors.joining(", "));
System.out.println("Result: " + result);
// 使用count获取流中的元素数量
long count = list.stream().count();
System.out.println("Count: " + count);
// 使用min和max获取流中的最小和最大元素
Optional<Integer> min = list.stream().min(Integer::compare);
Optional<Integer> max = list.stream().max(Integer::compare);
System.out.println("Min: " + min.orElse(0));
System.out.println("Max: " + max.orElse(0));
// 使用anyMatch判断流中是否存在满足条件的元素
boolean anyMatch = list.stream().anyMatch(n -> n > 3);
System.out.println("Any Match: " + anyMatch);
// 使用findFirst获取第一个元素
Optional<Integer> firstElement = list.stream().findFirst();
System.out.println("First Element: " + firstElement.orElse(-1));
1
2
3
4
5
[1, 2, 3, 4, 5]
Sum: 15
Result: 1, 2, 3, 4, 5
Count: 5
Min: 1
Max: 5
Any Match: true
First Element: 1