jdk1.8 List集合Stream流式处理
- 一、介绍(为什么需要流Stream,能解决什么问题?)
- 二、创建流Stream
- 三、中间操作
- 四、终端操作
-
- 4.0 foreach(常用于处理List、Map等数据)
- 4.1 收集(Collect)
-
- 4.1.1 转换为List
- 4.1.2 转换为Set
- 4.1.3 分组汇总(Grouping)
- 4.1.4 toMap(转成Map)
- 4.1.5 reducing 规约(此处只讨论简单使用,重载方法待研究)
- 4.1.6 计数(Counting)
- 4.1.7 汇总求和(Summing)
- 4.1.8 汇总对象(IntSummaryStatistics)
- 4.1.9 获取单个元素 maxBy、 minBy
- 4.1.10 toCollection()
- 4.1.11 mapping
- 4.1.12 joining
- 4.1.13 partitioningBy
- 4.1.14 collectingAndThen
- 4.2. 自定义收集器
- 4.3 归约(Reduce)
- 4.4 查找元素 findFirst、findAny
- 4.5 匹配 anyMatch、allMatch、noneMatch
- 4.6 count max min
- 五、并行流
- 六、总结
一、介绍(为什么需要流Stream,能解决什么问题?)
Java 8 引入了一个新的抽象层——Stream API,它允许你以声明性方式处理数据集合(包括数组、集合等)。Stream API 提供了一种高效且易于表达的方式来处理数据集合,包括过滤、排序、映射和归约等操作。这种处理方式极大地提高了代码的可读性和可维护性,同时也提升了处理大量数据的性能。
1.1 什么是 Stream?
Stream(流)是 Java 8 引入的一个关键抽象概念,它代表了一个来自数据源的元素队列并支持聚合操作。和迭代器(Iterator)不同,Stream 不存储元素;它们是源到聚合操作的中间桥梁,其操作的执行是延迟的,即只有在需要结果时才执行。
在日常编程中,会经常进行数据(如List、数组)的处理,在没有stram流时,我们的一般操作方式显得比较臃肿,不够优雅简洁。代码如下(原写法):
List<String> list = new ArrayList<>();
Collections.addAll(list,"赵子龙","关云长","黄忠","张良","张翼德");
ArrayList<String> list1 = new ArrayList<>();
list.forEach(s -> {
if(s.startsWith("张")) {
list1.add(s);
}
});
list1.forEach(s -> System.out.println(s));
stream流式写法
List<String> list = new ArrayList<>();
Collections.addAll(list,"赵子龙","关云长","黄忠","张良","张翼德");
list.stream().filter(p -> p.startsWith("张")).forEach(System.out::println);
这个例子也告诉我们Stream流写法更加简洁优雅。
流式操作三部曲如下图所示:

如上图所示,概括的讲,可以将stream流操作分为3种类型:
- 创建Stream
- Stream中间操作
- 终止Stream(终端操作)
每个Stream管道操作类型都包含若干API方法,先列举下各个API方法的功能介绍。
1.2 常见的创建Stream方法
在Java中,创建Stream的方法多种多样,可以从各种数据源生成Stream。以下是一些常见的创建Stream的方法:
| 方法类型 | 示例代码 | 描述 |
|---|---|---|
| 集合转Stream | List<String> list = Arrays.asList("a", "b", "c");<br>Stream<String> stream = list.stream(); |
通过Collection接口(如List、Set等)的stream()方法创建Stream。 |
| 数组转Stream | String[] array = {"d", "e", "f"};<br>Stream<String> stream = Arrays.stream(array); |
通过Arrays类的stream(T[] array)静态方法,将数组转换为Stream。 |
| Stream类静态方法 | Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5); |
使用Stream类的of(T... values)静态方法,从一组值中创建Stream。 |
| 无限流生成 | Stream<Double> randomStream = Stream.generate(Math::random);Stream<Integer> infiniteStream = Stream.iterate(0, i -> i + 1); |
使用Stream类的generate(Supplier<T> s)和iterate(T seed, UnaryOperator<T> f)静态方法生成无限流。generate接受一个供应器(Supplier),每次需要新值时调用其get()方法;iterate接受一个初始值和一个函数,每次迭代时将当前值作为参数传递给函数,返回的结果作为下一次迭代的值。 |
| 空Stream | Stream<String> emptyStream = Stream.empty(); |
使用Stream类的empty()静态方法创建一个空的Stream。 |
| 构建器 | Stream.Builder<String> builder = Stream.builder();<br>builder.add("Java");<br>builder.add("Python");<br>Stream<String> stream = builder.build(); |
使用Stream.Builder来构建复杂的Stream。首先创建一个Builder对象,然后调用其add方法添加元素,最后调用build方法生成Stream。 |
1.3 常见的中间操作
| 操作名称 | 操作方法 | 描述 |
|---|---|---|
| 过滤 | filter(Predicate<? super T> predicate) |
通过给定的谓词(predicate)测试元素,保留使谓词返回true的元素 |
| 映射 | map(Function<? super T, ? extends R> mapper) |
将每个元素映射到其对应的转换结果上,转换结果可以是一个新的类型 |
| 扁平映射 | flatMap(Function<? super T, ? extends Stream<? extends R>> mapper) |
类似于map,但每个元素都可以被映射成一个Stream,然后所有这些Stream会被合并成一个Stream |
| 排序 | sorted() / sorted(Comparator<? super T> comparator) |
对流中的元素进行排序,可以选择自然排序或自定义排序器 |
| 截断 | limit(long maxSize) |
限制流中元素的数量,使其不超过给定的最大值 |
| 跳过 | skip(long n) |
跳过流中的前n个元素 |
| 并行流 | .parallel() |
将顺序流转换为并行流,以便并行处理 |
| 串行流 | .sequential() |
将并行流转换回顺序流,以便顺序处理 |
| 去重 | distinct() |
返回一个由流中不同元素组成的流(基于元素的equals和hashCode方法) |
| 逐元素替换 | peek(Consumer<? super T> action) |
提供一个消费者函数,该函数会在处理每个元素时执行,但不影响流的内容 |
请注意,尽管peek操作可以看作是一种中间操作(因为它返回Stream本身),但它主要用于调试目的,并不改变流的内容或结构。
1.4 常见的终端操作
| 操作名称 | 操作方法 | 描述 |
|---|---|---|
| 匹配操作 | anyMatch(Predicate<? super T> predicate) |
是否存在至少一个元素匹配给定的谓词? |
allMatch(Predicate<? super T> predicate) |
是否所有元素都匹配给定的谓词? | |
noneMatch(Predicate<? super T> predicate) |
是否没有元素匹配给定的谓词? | |
| 查找操作 | findFirst() |
返回流中的第一个元素(作为Optional),如果流为空,则返回Optional.empty() |
findAny() |
返回流中的任意元素(作为Optional),对于并行流,行为可能不同 | |
| 归约操作 | reduce(BinaryOperator<T> accumulator) |
通过给定的归约操作(如求和、求积)将流中的所有元素组合起来,结果为Optional |
reduce(T identity, BinaryOperator<T> accumulator) |
类似于上一个,但提供了一个初始值,结果为T类型,而非Optional | |
| 收集操作 | collect(Collectors.toList()) / collect(Collectors.toSet()) 等 |
将流中的元素收集到一个列表、集合或其他容器中,Collectors类提供了多种收集器 |
| 最大值/最小值 | max(Comparator<? super T> comparator) / min(Comparator<? super T> comparator) |
根据给定的比较器找到流中的最大或最小元素,结果为Optional |
| 数组操作 | toArray(T[] generator) / toArray(IntFunction<T[]> generator) |
将流中的元素收集到一个数组中,需要提供一个数组生成器或类型信息 |
| 计数操作 | count() |
返回流中的元素数量,结果为long类型 |
| 遍历操作 | forEach(Consumer<? super T> action) |
对流中的每个元素执行给定的操作,这是一个终端操作,因为它有副作用(即执行操作) |
| 归纳操作 | summaryStatistics()(针对IntStream, LongStream, DoubleStream) |
返回包含各种统计数据的对象,如计数、平均值、最大值、最小值等 |
请注意,forEach虽然是一个终端操作,但它主要用于执行副作用(即不返回结果的操作),而不是为了获取结果。同样,reduce、collect和max/min等操作既可以看作是中间操作(因为它们可以在另一个流的上下文中使用,尽管这在实际中并不常见),也可以看作是终端操作(因为它们会触发流的执行并返回一个结果)。但是,在典型的用法中,我们更倾向于将它们视为终端操作。
二、创建流Stream
2.1 Collection的.stream()方法
public static void main(String[] args) {
// 1.list创建流
List<String> list = new ArrayList<>();
Collections.addAll(list, "赵子龙", "关云长", "黄忠", "张良", "张翼德");
// 创建一个Stream流
Stream<String> stream = list.stream();
// 使用流进行操作
stream.filter(p -> p.startsWith("张")).forEach(System.out::println);
// 2.map创建流(先转换成keySet或entrySet)
Map<Object, Object> map = new HashMap<>();
map.put("张三", 1);
map.put("李四", 8);
map.put("王五", 5);
// 2.1 keySet
map.keySet().stream().forEach(System.out::println);
map.keySet().stream().forEach(p -> System.out.println(p + "= " + map.get(p)));
// 2.2 entrySet
map.entrySet().stream().forEach(System.out::println);
}
示例中,用list.stream()创建流,用map装换成keySet或entrySet后用.stream()创建流。
2.2 数组创建流
int[] array = {
5,6,8,7,5,6,1,0};
// 1.从整数数组创建IntStream
IntStream intStream = Arrays.stream(array);
String[] strArray = {
"东邪","西毒","南帝","北丐","中神通"};
// 2.从字符串数组创建Stream
Stream<String> stringStream = Arrays.stream(strArray);
示例中,用Arrays.stream()创建流(IntStream 、Stream),为开发中最常用创建流的方式。
2.3 静态工厂方法
Stream.of(T… values):通过将一个可变参数的元素列表传递给Stream.of方法来创建一个包含这些元素的Stream流。这对于创建具有少量元素的流非常方便。
Stream.empty():使用Stream.empty()方法创建一个空的Stream流。
Stream.generate(Supplier s):通过提供一个Supplier函数来创建一个无限大小的Stream流,该函数会生成元素。通常,需要使用limit操作限制生成的元素数量。
Stream.iterate(T seed, UnaryOperator f):通过提供初始值(seed)和一个一元操作函数(UnaryOperator)来创建一个包含无限序列的Stream流。例如,可以使用Stream.iterate(0, n -> n + 1)来创建一个自然数序列的Stream流。
- Stream.of(T… values)创建流
// 1.String数组
String[] strArr = {
"东邪","西毒","南帝","北丐","中神通"};
Stream<String> strArrStream = Stream.of(strArr);
strArrStream.forEach(System.out::println);
// 2.注意:int类型数组,会将数组仅当作一个处理,如下图所示打印的是一个int[]的地址
int[] intArr = {
5,6,8,7,5,6,1,0};
Stream<int[]> intArrStram = Stream.of(intArr);
intArrStram.forEach(System.out::println);
打印如下:

- Stream.generate(Supplier s)创建流
// generate创建stream
Stream<Integer> randomIntStream = Stream.generate(() -> new Random().nextInt(100));
randomIntStream.limit(10).forEach(System.out::println);
2.4 Stream.builder
- Stream.builder创建Stream<Integer>
// 1.逐个添加1到10的整数
Stream.Builder<Integer> builder = Stream.builder();
for (int i = 0; i < 10; i++) {
builder.accept(i);
}
Stream<Integer> builderIntStream = builder.build();
- Stream.builder创建Stream<Long>
// 2.斐波那契数列的前10个数字
Stream.Builder<Long> builder1 = Stream.builder();
long a = 0; long b = 1;
int count = 10;
for (int i = 0; i < count; i++) {
builder1.accept(a);
long next = a + b;
a = b;
b = next;
}
Stream<Long> longStream = builder1.build();
2.5 从文件创建流
- 使用Files.lines方法创建文本文件的流
// 逐行读取,创建Stream
String path = "names.txt";
try {
Stream<String> lines = Files.lines(Paths.get(path));
List<String> names = lines.collect(Collectors.toList());
} catch (IOException e) {
throw new RuntimeException(e);
}
三、中间操作
基础代码创建list
List<User> userList = new ArrayList<>(


604

被折叠的 条评论
为什么被折叠?



