参考资料: Java入门到飞起
1. 引言
随着Java版本的不断演进,Java 8引入了Stream流,Stream流是一种用于处理集合数据的抽象概念,它允许开发者以声明式的方式处理数据,就像流水线上的工序一样,将各种操作串联起来完成复杂的数据转换和处理任务。而方法引用则进一步简化了Lambda表达式的使用,使得代码更加简洁明了。
本文将深入探讨Java Stream流和方法引用的核心概念、使用场景和最佳实践。我们将从基础概念入手,逐步讲解Stream流的创建方式、中间操作和终结操作。
2. Stream流
2.1 体验Stream流
案例需求
按照以下要求完成集合的创建和遍历:
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合
原始方式实现
jdk1.8使用List.of()会报错的,9版本以上是可以用;8版本的话,可以用add一点点加。
public class MyStream1 {
public static void main(String[] args) {
// 集合初始化
ArrayList<String> list1 = new ArrayList<>(List.of("张三丰","张无忌","张翠山","王二麻子","张良","谢广坤"));
// 筛选以"张"开头的元素
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if(s.startsWith("张")){
list2.add(s);
}
}
// 筛选长度为3的元素
ArrayList<String> list3 = new ArrayList<>();
for (String s : list2) {
if(s.length() == 3){
list3.add(s);
}
}
// 遍历输出
for (String s : list3) {
System.out.println(s);
}
}
}
Stream流实现
import java.util.ArrayList;
public class MyStream1 {
public static void main(String[] args) {
// 集合初始化
ArrayList<String> list1 = new ArrayList<>();
list1.add("张三丰");
list1.add("张无忌");
list1.add("张翠山");
list1.add("王二麻子");
list1.add("张良");
list1.add("谢广坤");
// 使用 Stream API 筛选并输出结果
list1.stream()
.filter(s -> s.startsWith("张"))
.filter(s -> s.length() == 3)
.forEach(System.out::println);
}
}
Stream流优势
- 代码简洁:一行代码完成传统方式多行才能完成的操作
- 可读性强:方法链直观表达数据处理流程
- 函数式编程:引入Java的函数式编程范式
2.2 Stream流的常见生成方式
Stream流思想
Stream流可以看作数据的"流水线",数据从源头进入,经过一系列处理,最终被消费。
生成方式
集合类型 | 生成方式 | 示例 |
---|---|---|
Collection | stream() 方法 | list.stream() |
Map | 转换为Set后获取流 | map.keySet().stream() |
数组 | Arrays.stream() | Arrays.stream(arr) |
多个数据 | Stream.of() | Stream.of(1,2,3) |
代码示例
public class StreamDemo {
public static void main(String[] args) {
// Collection生成流
List<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
// Map生成流
Map<String,Integer> map = new HashMap<>();
Stream<String> keyStream = map.keySet().stream();
// 数组生成流
String[] strArray = {"a", "b", "c"};
Stream<String> arrayStream = Arrays.stream(strArray);
// 多个数据生成流
Stream<String> ofStream = Stream.of("x", "y", "z");
}
}
2.3 Stream流中间操作方法
中间操作特点
- 返回新的Stream对象
- 可以链式调用多个中间操作
- 不立即执行,形成流水线
方法 | 说明 | 示例 |
---|---|---|
filter() | 过滤元素 | .filter(s -> s.startsWith("张")) |
limit() | 限制数量 | .limit(3) |
skip() | 跳过元素 | .skip(2) |
concat() | 合并流 | Stream.concat(s1, s2) |
distinct() | 去重 | .distinct() |
代码示例
package stream;
import java.util.ArrayList;
import java.util.stream.Stream;
/**
* 该类演示了如何使用 Stream 流对集合进行过滤、限制和跳过操作。
*/
public class StreamDemo {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<String>();
names.add("林青霞");
names.add("张曼玉");
names.add("王祖贤");
names.add("柳岩");
names.add("张敏");
names.add("张无忌");
// 过滤以"张"开头的名字
System.out.println("1.过滤姓张的人--------");
names.stream()
.filter(s -> s.startsWith("张"))
.forEach(System.out::println);
System.out.println("--------");
// 限制前3个元素
System.out.println("2.限制前三个元素--------");
names.stream()
.limit(3)
.forEach(System.out::println);
System.out.println("--------");
// 跳过前2个元素
System.out.println("3.跳过第二个元素--------");
names.stream()
.skip(2)
.forEach(System.out::println);
System.out.println("--------");
// 合并两个流并去重
System.out.println("4. 合并2,3并去重--------");
Stream<String> s1 = names.stream().limit(3);
Stream<String> s2 = names.stream().skip(2);
Stream.concat(s1, s2).distinct().forEach(System.out::println);
}
}
结果输出:
1.过滤姓张的人--------
张曼玉
张敏
张无忌
--------
2.限制前三个元素--------
林青霞
张曼玉
王祖贤
--------
3.跳过第二个元素--------
王祖贤
柳岩
张敏
张无忌
--------
4. 合并2,3并去重--------
林青霞
张曼玉
王祖贤
柳岩
张敏
张无忌
PS E:\Trae\Traeproject>
2.4 Stream流终结操作方法
终结操作特点
- 执行后会关闭流,不能继续使用
- 通常用于获取结果或执行副作用操作
方法 | 说明 | 示例 |
---|---|---|
forEach() | 遍历元素 | .forEach(System.out::println) |
count() | 计数 | .count() |
代码示例
public class StreamDemo {
public static void main(String[] args) {
List<String> names = List.of("张三丰", "张无忌", "张翠山");
// 遍历元素
names.stream()
.forEach(System.out::println);
// 计数
long count = names.stream()
.filter(s -> s.startsWith("张"))
.count();
System.out.println("以张开头的人数: " + count);
}
}
2.5 Stream流的收集操作【应用】
收集操作
将Stream中的元素收集到集合或其他数据结构中
方法 | 说明 | 示例 |
---|---|---|
toList() | 收集到List | .collect(Collectors.toList()) |
toSet() | 收集到Set | .collect(Collectors.toSet()) |
toMap() | 收集到Map | .collect(Collectors.toMap(...)) |
代码示例
public class StreamDemo {
public static void main(String[] args) {
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// 收集到List
List<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
// 收集到Set
Set<Integer> oddNumbers = numbers.stream()
.filter(n -> n % 2 != 0)
.collect(Collectors.toSet());
// 收集到Map
List<String> personList = List.of("张三,25", "李四,30", "王五,28");
Map<String, Integer> personMap = personList.stream()
.filter(s -> {
String[] parts = s.split(",");
return Integer.parseInt(parts[1]) >= 25;
})
.collect(Collectors.toMap(
s -> s.split(",")[0],
s -> Integer.parseInt(s.split(",")[1])
));
}
}
2.6 Stream流综合练习
案例需求
处理演员数据:
- 男演员:名字为3个字的前三人
- 女演员:姓林的,并且不要第一个
- 合并结果创建Actor对象
代码实现
// Actor类
public class Actor {
private String name;
public Actor(String name) {
this.name = name;
}
// getter/setter...
@Override
public String toString() {
return "Actor{name='" + name + "'}";
}
}
// 测试类
public class StreamTest {
public static void main(String[] args) {
ArrayList<String> manList = List.of("周润发", "成龙", "刘德华", "吴京", "周星驰", "李连杰");
ArrayList<String> womanList = List.of("林心如", "张曼玉", "林青霞", "柳岩", "林志玲", "王祖贤");
// 处理男演员
Stream<String> manStream = manList.stream()
.filter(s -> s.length() == 3)
.limit(3);
// 处理女演员
Stream<String> womanStream = womanList.stream()
.filter(s -> s.startsWith("林"))
.skip(1);
// 合并并创建Actor对象
Stream.concat(manStream, womanStream)
.map(Actor::new) // 方法引用创建Actor
.forEach(System.out::println); // 方法引用打印
}
}
结果输出:
Actor:{name='周润发'}
Actor:{name='刘德华'}
Actor:{name='周星驰'}
Actor:{name='林青霞'}
Actor:{name='林志玲'}