一、Stream流
1.1 初体验

java
package com.lkbhua.StreamDemo;
import java.util.ArrayList;
public class demo1 {
public static void main(String[] args) {
/*
创建集合添加元素,完成以下需求:
1、把所有以"张"开头的元素存储到新集合当中
2、把"张"开头的,长度为3的元素存储到新集合当中
3、遍历并打印最终的结果
*/
ArrayList<String> list1 = new ArrayList<>();
list1.add("张三");
list1.add("张三丰");
list1.add("张无忌");
list1.add("李四");
list1.add("马思维");
// 不用stream流的方法
/*
// 1、把所有以"张"开头的元素存储到新集合当中
ArrayList<String> list2 = new ArrayList<>();
for(String name : list1){
if(name.startsWith("张")){
list2.add(name);
}
}
System.out.println(list2); // [张三, 张三丰, 张无忌]
// 2、把"张"开头的,长度为3的元素存储到新集合当中
ArrayList<String> list3 = new ArrayList<>();
for(String name : list2){
if(name.length() == 3){
list3.add(name);
}
}
System.out.println(list3); // [张三, 张无忌]
// 3、遍历并打印最终的结果
for(String name : list3){
System.out.println(name);
}
*/
System.out.println("-----------------");
// 用stream流
list1.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
}
}
1.1.1什么是 Stream 流?
Stream 是 Java 中对数据源 (如集合、数组等)进行一系列操作的抽象表示。它不是数据结构,也不存储数据,而是对数据进行计算的一系列操作管道。
特点:
- 不存储数据:只对数据源进行操作。
- 函数式编程风格:使用 Lambda 表达式。
- 惰性求值(Lazy Evaluation):中间操作不会立即执行,只有遇到终结操作才会真正执行。
- 不可重复使用:一个 Stream 只能被消费一次,再次使用会抛出异常。
- 支持并行处理 :可通过
parallel()转为并行流提升性能。
1.2 Stream流的思想
Stream 的核心思想是"流水线(Pipeline)":
- 源头(Source):提供原始数据(集合、数组、I/O 等)。
- 中间操作(Intermediate Operations):对数据进行转换、过滤等,返回新的 Stream(可链式调用)。
- 终结操作(Terminal Operation):触发整个流水线执行,产生结果或副作用(如打印、收集等)。
类似工厂流水线:原材料 → 加工 → 成品

首先把要操作的数据放到流水线上,进行过滤操作,其他数据就舍弃不要,最后进行输出操作。

1.3 使用步骤


1.3.1 如何放到流水线上
注意:
① 双列集合不能直接转成Stream流,需要通过keySet()或者entrySet()转换成单列集合,才能获取Stream流;
② 一堆零散的数据可以通过Stream接口中的静态方法放到流水线上,但是这些数据需要同种数据类型的。

java
package com.lkbhua.StreamDemo;
import java.util.*;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class demo2 {
public static void main(String[] args) {
/* Stream流的使用步骤1:
如何将数据放到Stream流上?
单列集合 default Stream<E> stream() Collection中的默认方法
双列集合 无 无法直接使用stream流
数组 public static <T> Stream<T> stream(T[] array) Arrays工具类中的静态方法
一堆零散的数据 public static <T> Stream<T> of(T...values) Stream接口中的静态方法
*/
// 1、单列集合获取Stream流
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "a", "b", "c", "d", "e", "f");
/*// 获取Stream流,并且把数据放到Stream流中
Stream<String> stream1 = list1.stream();
// 使用终结方法遍历
stream1.forEach(new Consumer<String>() {
@Override
public void accept(String s) {
// s: Stream流中的数据
System.out.println(s);
}
});
*/
list1.stream().forEach(s -> System.out.println(s));
}
public static void method2(){
// 2、双列集合获取Stream流
// 无法直接创建,需要转换成单列集合
// 创建双列集合
HashMap<String,String> map = new HashMap<>();
map.put("1", "a");
map.put("2", "b");
map.put("3", "c");
map.put("4", "d");
// case1:通过键或者值获取stream流
Stream<String> stream = map.values().stream();
Stream<String> stream1 = map.keySet().stream();
// case2:通过键值对获取stream流
Stream<Map.Entry<String, String>> stream2 = map.entrySet().stream();
}
public static void method3(){
// 3、数组获取Stream流
// 基本数据类型
int[] arr = {1,2,3,4,5,6,7,8,9,10};
// 获取Stream流
Arrays.stream(arr).forEach(s -> System.out.println(s));
// 引用数据类型
String[] arr1 = {"a","b","c","d","e","f"};
Arrays.stream(arr1).forEach(s -> System.out.println(s));
}
public static void method4(){
// 4、一堆零散的数据获取Stream流
// 基本数据类型
Stream<String> stream = Stream.of("a", "b", "c", "d", "e", "f");
stream.forEach(s -> System.out.println(s));
// 引用数据类型
Stream<Integer> stream1 = Stream.of(1, 2, 3, 4, 5, 6);
stream1.forEach(s -> System.out.println(s));
}
}

1.3.2 中间方法
中间方法返回一个新的 Stream,支持链式调用,不会立即执行。

✅ 所有中间操作都是惰性的,只有遇到终结操作才执行。
java
package com.lkbhua.StreamDemo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Predicate;
public class demo3 {
public static void main(String[] args) {
/* Stream流的中间方法1
filter 过滤
limit 获取前几个元素
skip 跳过前几个元素
注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
注意2:修改Stream流中的数据,不会影响原来的集合或者数组中的数据
*/
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "a", "b", "c", "d", "e", "f");
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
// true: 表示当前的值留下
// false: 表示当前的值去掉
if(s.equals("a") || s.equals("c") || s.equals("e")){
return true;
}
return false;
}
}).forEach(s -> System.out.println(s));
System.out.println("----------------");
list.stream().
filter(s -> s.equals("a") || s.equals("c") || s.equals("e")).
forEach(s -> System.out.println(s));
// 这样可以提高链式编程的阅读性
System.out.println("----------------");
System.out.println(list);
// limit获取前几个元素
list.stream().
limit(3).
forEach(s -> System.out.println(s));
System.out.println("----------------");
// skip跳过前几个元素
list.stream().
skip(3).
forEach(s -> System.out.println(s));
}
}
1.3.3 终结方法
处理完的数据,放到集合或者数组当中存储。终结方法会触发流的执行,并产生结果或副作用。执行后流被关闭,不能复用。

⚠️ 终结操作只能有一个!
java
package com.lkbhua.StreamDemo;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.function.IntFunction;
public class demo6 {
public static void main(String[] args) {
/* stream的终结方法
void forEach(Consumer action) 遍历
long count() 统计数量
toArray() 收集流中的数据,放到数组当中
*/
ArrayList<String> list = new ArrayList<String>();
Collections.addAll(list, "a", "b", "c", "d", "e", "f");
// 遍历
// Consumer的泛型:表示流中数据的类型
// accept方法的形参s:依次表示流里面的每一个数据
// 方法体:对每一个数据进行操作(这里是:打印)
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
list.stream().forEach(s -> System.out.println(s));
System.out.println("----------------");
ArrayList<String> list1 = new ArrayList<String>();
Collections.addAll(list1, "a", "b", "c", "d", "e", "f");
// 统计数量
long count = list1.stream().count();
System.out.println(count);
System.out.println("----------------");
ArrayList<String> list2 = new ArrayList<String>();
Collections.addAll(list2, "a", "b", "c", "d", "e", "f");
// 收集流中的数据,放到数组当中
// Object[] array = list1.stream().toArray();
// IntFunction的泛型:具体类型的数组
// apply的形参:流中数据的个数,要跟数组的长度保持一致
// 返回值: 具体类型的数组
// 方法体:就是创建数组
// toArray方法的参数的作用:负责创建一个指定类型的数组
// toArray方法的底层:会依次得到流里面的每一个数据,并把数据放到数组当中
// toArray方法的返回值:是一个装着流里面所有数据的数组
String[] array = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
System.out.println(Arrays.toString( array));
}
}
1.3.4 收集方法
collect(Collector) 是最强大的终结操作之一,用于将流结果收集到集合、字符串、分组等。
常用 Collectors:
| 方法 | 用途 |
|---|---|
toList() |
收集为 List |
toSet() |
收集为 Set |
toMap(keyMapper, valueMapper) |
收集为 Map |
joining() / joining(delimiter) |
拼接字符串 |
groupingBy(classifier) |
分组(Map<K, List>) |
partitioningBy(predicate) |
分区(Map<Boolean, List>) |
summingInt/mapping/averagingDouble |
聚合统计 |
java
package com.lkbhua.StreamDemo;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class demo7 {
public static void main(String[] args) {
/*
collect(Collector collector) 收集流中的数据,放到集合当中(List、Map、Set)
注意点:
如果我们要收集到Map集合当中,键不能重复,否则会报错
*/
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"a-男-1","b-男-2","c-女-3","d-男-4","e-女-5","f-女-6","g-男-7","h-女-8");
// 收集到List集合当中
// 需求:找出里面的男性并且收集
// 能用固定数据去调用equals的方法就用固定数据去调用
List<String> newlist1 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());
System.out.println(newlist1);
System.out.println("----------------");
// 收集到Set集合当中
// Set集合:无序,不能重复
// 需求:找出里面的男性并且收集
// 能用固定数据去调用equals的方法就用固定数据去调用
Set<String> newSet2 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());
System.out.println(newSet2);
System.out.println("----------------");
// 收集到Map集合当中
// 注意:谁做为键,谁做为值
// 键:字母,值:数字
Map<String,Integer> newMap3 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(new Function<String, String>() {
/*
* toMap:参数一:键的生成规则,
* 参数二:值的生成规则
* 参数一:
* Function泛型一: 表示流中每一个数据的类型
* Function泛型二: 表示Map集合中键的数据类型
* 方法apply的形参:依次表示流里的每一个数据
* 方法体:生成键
* 返回值:已经生成的键
* 参数二:
* Function泛型一: 表示流中每一个数据的类型
* Function泛型二: 表示Map集合中值的数据类型
* 方法apply的形参:依次表示流里的每一个数据
* 方法体:生成值
* 返回值:已经生成的值
* */
@Override
public String apply(String s) {
return s.split("-")[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
return Integer.parseInt(s.split("-")[2]);
}
}));
System.out.println(newMap3);
System.out.println("----------------");
Map<String,Integer> newMap4 = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2])));
System.out.println(newMap4);
System.out.println("----------------");
}
}
