Stream API:Java 8 中的数据流式处理指南
Hello , 大家好 , 本篇文章给大家讲解的是 Java 8 中的 Stream 流 .
在 Java 8 中 , 引入了 Streams API,它允许以声明式方式处理数据集合。Stream API 提供了一种高级迭代器,可以用来执行序列化操作,如筛选、转换、聚合等。它支持并行操作,可以利用多核处理器来加速数据处理。
在日常开发过程中 , 使用 stream 流能够显著提升开发效率 , 极大提升生产力 , 非常推荐使用 .
一 . 体验 Stream 流
需求 : 按照下面的要求完成集合的创建和遍历
- 创建一个集合 , 存储多个字符串元素
- 把集合中所有以 “张” 开头的元素存储到一个新的集合
- 把 “张” 开头的集合中的长度为 3 的元素存储到另外一个新的集合中
- 遍历上一步得到的集合
那我们使用原始的方式 , 代码就是这个样子
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张四丰");
list.add("张五");
list.add("李四");
list.add("王二麻子");
List<String> result = new ArrayList<>();
// 1. 遍历 list 集合
for (String s : list) {
// 2. 判断元素是否以 "张" 开头
if (s.startsWith("张")) {
// 3. 判断元素长度是否是 3
if (s.length() == 3) {
// 4. 将元素添加到 result 集合中
result.add(s);
}
}
}
System.out.println(result);
}
}
如果我们使用的是 Stream 流的形式 , 那代码就会被简化成这个样子
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("张三丰");
list.add("张四丰");
list.add("张五");
list.add("李四");
list.add("王二麻子");
// Stream 流的写法
List<String> result = new ArrayList<>();
list.stream()
.filter(name -> name.startsWith("张"))
.filter(name -> name.length() == 3)
.forEach(name -> System.out.println(name));
}
}
那我们通过这个案例就可以看出 Stream 流的作用就是针对集合进行功能简化开发 , 并且 Stream 流的使用通常需要搭配 Lambda 表达式来使用 .
那 Stream 流我们就可以理解成流水线的过程 , 一步一步地筛选掉不满足条件的数据 .
那我们也可以得到 Stream 流的三类方法
- 获取 Stream 流 : 创建一条流水线 , 并把数据放到流水线上准备进行操作
- 中间方法 : 流水线上的操作 , 一次操作结束之后还可以继续进行其他操作
- 终结方法 : 一个 Stream 流只能有一个终结方法 , 是流水线上最后一道操作
二 . 获取 Stream 流对象
获取 Stream 流就是创建一条流水线 , 并把数据放到流水线上准备进行操作 .
2.1 单列集合
单列集合就是我们之前学习过的 Collection (List、Set …)
我们可以使用 Collection 接口中的默认方法 stream() 生成流
import java.util.ArrayList;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method01();
}
private static void method01() {
// 单列集合
ArrayList<String> list = new ArrayList<>();
list.add("甄嬛");
list.add("沈眉庄");
list.add("安陵容");
// 获取 Stream 对象
// 直接调用 stream 方法即可
Stream<String> stream = list.stream();
// 打印 stream 流中的内容
stream.forEach(s -> System.out.println(s));
}
}
而我们一般情况下不会将 stream 流拆分开来写
import java.util.ArrayList;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method01();
}
private static void method01() {
// 单列集合
ArrayList<String> list = new ArrayList<>();
list.add("甄嬛");
list.add("沈眉庄");
list.add("安陵容");
// 获取 Stream 对象
// 直接调用 stream 方法即可
list.stream().forEach(s -> System.out.println(s));
}
}
我们看一下运行结果
2.2 哈希表集合
哈希表集合需要间接的生成流 , 可以先通过 keySet 或者 entrySet 获取一个 Set 集合 , 再获取 Stream 流
import java.util.ArrayList;
import java.util.HashMap;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method02();
}
private static void method02() {
// 双列集合
HashMap<String, Integer> map = new HashMap<>();
map.put("甄嬛", 18);
map.put("沈眉庄", 19);
map.put("安陵容", 20);
// 获取 Stream 流
// 双列集合不能直接获取 Stream 流
// 先获取到所有的键, 再把这个 Set 集合中所有的键放到 Stream 流中
map.keySet().stream().forEach(s -> System.out.println(s));// 打印出所有的键
// 先获取到所有的键值对对象, 再把这个 Set 集合中所有的键值对对象都放到 Stream 流中
map.entrySet().stream().forEach(s -> System.out.println(s));// 打印出所有的键值对对象
}
}
运行一下 , 看一下效果
2.3 数组
数组可以通过 Arrays 中的静态方法 stream 生成流
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method03();
}
private static void method03() {
// 数组
int[] arr = {1, 2, 3, 4, 5};
// 获取 Stream 流
Arrays.stream(arr).forEach(s -> System.out.println(s));
}
}
2.4 同种数据类型的多个元素
比如 :
- 1 , 2 , 3 , 4 , 5 …
- “aaa” , “bbb” , “ccc” , “ddd” …
我们需要使用 Stream.of(T… values) 生成流
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method04();
}
private static void method04() {
// 同种数据类型的多个数据
// 将同一类型的数据放到 of 中即可
Stream.of(1, 2, 3, 4, 5, 6, 7, 8).forEach(s -> System.out.println(s));
}
}
2.5 小结
单列集合 : 集合对象.stream()
双列集合 : 不能直接获取 , 需要间接获取
- 获取键的集合 : 集合对象.keySet().stream();
- 获取键值对的集合 : 集合对象.entrySet().stream();
数组 : Arrays.stream(数组名称)
同种数据类型的多个数据 : Stream.of(多个数据);
三 . 中间方法
中间方法指的就是流水线上的操作 . 一次操作完毕之后 , 还可以继续进行其他的操作 .
3.1 filter 方法
filter 方法用于对流中的数据进行过滤 . 对给定的参数进行判断 , 返回一个布尔值
3.1.1 匿名内部类
这种方式需要我们传入一个 Predicate 匿名内部类对象
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method05();
}
// 需求 : 筛选出姓张的用户
private static void method05() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("李四");
// 获取 Stream 流
// 使用 filter 可以设置筛选条件
// 这个方法需要我们传入一个 Predicate 匿名内部类对象
list.stream().filter(
// filter 方法获取流中的每一个数据
// 而 test 方法中的 s, 就一次表示流中的每一个数据
// 我们只需要在 test 方法中对 s 进行判断即可
new Predicate<String>() {
@Override
public boolean test(String s) {
boolean result = s.startsWith("张");
return result;
}
}
).forEach(s -> System.out.println(s));
}
}
3.1.2 lambda 表达式
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method06();
}
// 需求 : 筛选出姓张的用户
private static void method06() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("李四");
// 获取 Stream 流
// 使用 filter 可以设置筛选条件
list.stream().filter(
(String s) -> {
boolean result = s.startsWith("张");
return result;
}
).forEach(s -> System.out.println(s));
}
}
那我们还可以对这个 lambda 表达式进一步简化
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method06();
}
// 需求 : 筛选出姓张的用户
private static void method06() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("李四");
// 获取 Stream 流
// 使用 filter 可以设置筛选条件
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
}
}
3.2 limit 方法
limit 方法是用来截取指定参数个数的数据
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method07();
}
private static void method07() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
list.add("张碧晨");
list.add("张信哲");
list.add("李四");
list.add("王五");
list.add("赵六");
// 截取指定参数个数的数据
list.stream().limit(2).forEach(s -> System.out.println(s));
}
}
3.3 skip 方法
skip 方法是跳过指定参数个数的数据
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method07();
}
private static void method07() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
list.add("张碧晨");
list.add("张信哲");
list.add("李四");
list.add("王五");
list.add("赵六");
// 跳过指定参数个数的数据
// 去除前面的, 保留后面的
list.stream().skip(2).forEach(s -> System.out.println(s));
}
}
3.4 concat 方法
concat 方法是合并 a 和 b 两个流为一个流
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method07();
}
private static void method07() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
list.add("张碧晨");
list.add("张信哲");
list.add("李四");
list.add("王五");
list.add("赵六");
List<String> list2 = new ArrayList<>();
list2.add("张三丰");
list2.add("张无忌");
// 合并两个流为一个流
Stream<String> stream1 = list.stream();
Stream<String> stream2 = list2.stream();
Stream<String> concat = Stream.concat(stream1, stream2);
concat.forEach(s -> System.out.println(s));
}
}
那我们还可以进行简写
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method07();
}
private static void method07() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
list.add("张碧晨");
list.add("张信哲");
list.add("李四");
list.add("王五");
list.add("赵六");
// 截取指定参数个数的数据
// 保留前面的, 去除后面的
// list.stream().limit(2).forEach(s -> System.out.println(s));
// 跳过指定参数个数的数据
// 去除前面的, 保留后面的
// list.stream().skip(2).forEach(s -> System.out.println(s));
List<String> list2 = new ArrayList<>();
list2.add("张三丰");
list2.add("张无忌");
// 合并两个流为一个流
Stream.concat(list.stream(), list2.stream()).forEach(s -> System.out.println(s));
}
3.5 distinct 方法
distinct 方法用来去除流中重复的元素 , 底层依赖 hashCode 方法和 equals 方法
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method08();
}
private static void method08() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
// 设置一些重复数据
list.add("张三");
list.add("张三");
list.add("张三");
list.add("张三");
// 去除流中重复的元素
// 底层依赖 hashCode 和 equals 实现的
list.stream().distinct().forEach(s -> System.out.println(s));
}
}
四 . 终结方法
一个 Stream 流只能有一个终结方法
常见的终结操作方法如下 :
- forEach : 对流中的每个元素进行操作
- count : 返回流中的元素数量
4.1 forEach 方法
forEach 最基础的方法的形参是一个 Consumer 匿名内部类
我们还可以使用 lambda 表达式来进行缩写
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method09();
}
private static void method09() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
list.add("李四");
list.add("王五");
list.add("赵六");
list.stream().forEach(new Consumer<String>() {
// 在 forEach 方法的底层, 会循环获取到流中的每个数据, 并循环调用 accept 方法, 将每一个数据传递给 accept 方法
// s 就依次表示了流中的每一个数据
@Override
public void accept(String s) {
// 对数据的处理可以直接写到 accept 方法中
System.out.println(s);
}
});
// lambda 表达式简写
list.stream().forEach(
(String s) -> {
System.out.println(s);
}
);
// lambda 表达式更进一步简写
list.stream().forEach(s -> System.out.println(s));
}
}
4.2 count 方法
count 方法用于返回流中的元素个数
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method10();
}
private static void method10() {
List<String> list = new ArrayList<>();
list.add("张凯丽");
list.add("张韶涵");
list.add("张学友");
list.add("张靓颖");
list.add("李四");
list.add("王五");
list.add("赵六");
// 返回流中的元素个数
long count = list.stream().count();
System.out.println(count);
}
}
五 . 收集操作
需求 : 过滤元素并遍历集合
我们定义一个集合 , 并且添加一些整数 1、2、3、4、5、6、7、8、9、10 .
将集合中的奇数删除 , 只保留偶数 .
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method11();
}
// 我们定义一个集合, 并且添加一些整数 1、2、3、4、5、6、7、8、9、10 .
// 将集合中的奇数删除, 只保留偶数 .
private static void method11() {
List<Integer> list = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
// 基本写法
list.stream().filter(
// 参数为 Integer 类型
(Integer i) -> {
// 在这个位置设置条件
return i % 2 == 0;
}
);
// 简化写法
list.stream().filter(num -> num % 2 == 0).forEach(num -> System.out.println(num));
}
}
但是我们此时打印一下原来的集合
结论 : 在 Stream 流中无法直接修改集合、数组等数据源中的数据 .
但是如果我们使用 Stream 流的方式操作完毕之后 , 那我还想把流中的数据保存起来 , 该怎么办呢 ?
我们需要使用 Collectors 这个工具类
- toList() : 把元素收集到 List 集合中
- toSet() : 把元素收集到 Set 集合中
- toMap() : 把元素收集到 Map 集合中
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class Demo2 {
public static void main(String[] args) {
method12();
}
private static void method12() {
// 10 这个数据是重复的
List<Integer> list1 = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 10, 10, 10));
// filter 方法负责过滤数据
// collect 只负责获取流中剩余的数据, 但是他并不负责创建容器, 也不负责把数据添加到容器中
// Collectors.toList() 在底层会创建一个 List 集合, 并且把所有的数据添加到 List 集合中
List<Integer> list2 = list1.stream().filter(num -> num % 2 == 0).collect(Collectors.toList());
System.out.println(list2);
// Collectors.toSet() 在底层会创建一个 Set 集合, 并且把所有的数据添加到 Set 集合中
Set<Integer> list3 = list1.stream().filter(num -> num % 2 == 0).collect(Collectors.toSet());
System.out.println(list3);
}
}
我们看一下运行结果
对于 Java8 中的 Stream 流的介绍就到这里 , 如果对你有帮助的话 , 还请一键三连支持~