stream 流
文章目录
- 1. stream 流初体验
- 2. 获取 Stream 流
- 2.1 单列集合 获取 stream 流
- 2.2 双列集合 获取 Stream 流
- 2.3 数组获取 stream 流
- 2.4 一堆零散数据获取 Stream 流
- 3. Stream 流的中间方法
- 3.1 filter 过滤方法
- 3.2 limit 和 skip 方法
- 3.3 distinct 去重方法
- 3.4 cancat 方法
- 3.5 map 方法
- 4. Stream 流的终结方法
- 4.1 forEach 遍历方法
- 4.2 count 统计方法
- 4.3 toArray 收集方法 (数组)
- 4.4 Collect 收集方法 (集合)
- 5. Stream 流练习
- 5.1 数据过滤
- 5.2 自定义对象过滤并收集
前言 :
为啥要学习Steam , Stream(流)是一种在编程中处理数据序列的概念。它提供了一种高效、便捷的方式来对数据进行操作和处理。
Stream 可以看作是一个元素序列,可以是数组、集合或输入/输出资源等。与传统的集合操作不同,Stream 并不是存储数据的容器,而是通过一系列的操作来处理数据并生成结果。
使用 Stream,我们可以进行各种数据操作,例如过滤、映射、排序、聚合等。这些操作可以按照链式的方式组合,形成一个操作流水线。每个操作都会在数据上进行处理,并将结果传递给我们
本文是基于 B站 Stream 流学习的笔记 相关学习视频
1. stream 流初体验
这里 创建一个集合元素 完成一下需求
- 把所有以
张
开头的元素存储到 新集合中 - 把所有
张
开头的 ,长度为 3 的元素 在 存储到新的集合中 - 遍历打印最终结果
按照以往 会这么写
package com.rabbitmq.springboot_rabbitmq.Test;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
public class StreamTest {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三丰", "王五", "大古", "迪迦", "飞鸟信", "戴拿", "张小马仔");
// 1. 把所有 张开头的 元素存储到 新的集合中
ArrayList<String> list2 = new ArrayList<>();
for (String name : list) {
if (name.startsWith("张")) {
list2.add(name);
}
}
System.out.println(list2);
System.out.println("----------------------------------------------");
// 2.把所有 `张` 开头的 ,长度为 3 的元素 在 存储到新的集合中
ArrayList<String> list3 = new ArrayList<>();
for (String name : list2) {
if (name.length() == 3) {
list3.add(name);
}
}
System.out.println(list3);
System.out.println("----------------------------------------------");
// 3. 遍历打印最终结果
for (String name : list3) {
System.out.println(name);
}
}
}
效果:
这里如果我们使用 stream 流 就只需要写一行代码就可以完成上面的 这么多代码.
public class StreamTest {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三丰", "王五", "大古", "迪迦", "飞鸟信", "戴拿", "张小马仔");
list.stream().filter(name -> name.startsWith("张")).filter(name -> name.length() == 3).forEach(name -> System.out.println(name));
}
}
这里是不是就非常的高效,代码非常的精简,下面我们就来开启对stream 流的学习.
2. 获取 Stream 流
这里先来简单看看 stream 流的使用步骤 :
- 获得一条 stream 流 (可以理解为是一条流水线) , 并把数据放上去
- 使用
中间方法 (调用后还能调用其他方法)
对流水线进行操作 - 使用
终结方法 (调用后就不能再调用其他方法了)
对流水线的数据进行操作.
有了步骤 ,就来学习如何获取到 stream 流
引用 :
这里的双列集合 就为 : map 等
代码演示 :
2.1 单列集合 获取 stream 流
package com.example.demo.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
import java.util.stream.Stream;
public class StreamDome {
public static void main(String[] args) {
// 单列集合获取 Stream 流
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "c", "d", "e");
// 获取到一条流水线 , 并把集合中的 数据放到流水线上
Stream<String> stream = list.stream();
// 使用 终结方法 打印一下流水线上的所有参数
// stream.forEach(new Consumer<String>() {
// @Override
// public void accept(String s) {
// // s: 依次表示流水线上的每个数据
// System.out.println(s);
// }
// });
stream.forEach(s -> System.out.println(s));
}
}
2.2 双列集合 获取 Stream 流
注意: 双列集合是不能直接获取到 Stream 流的 需要先将双列集合转为单列集合才能获取
代码:
package com.example.demo.Test;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Stream;
public class StreamDome2 {
public static void main(String[] args) {
// 1. 创建双列集合
HashMap<String, Integer> hm = new HashMap<>();
// 2. 添加数据
hm.put("aaa", 11);
hm.put("bbb", 22);
hm.put("ccc", 33);
hm.put("ddd", 44);
hm.put("fff", 55);
// 3. 获取 stream 流
Stream<String> stream = hm.keySet().stream();
stream.forEach(s -> System.out.println(s));
Stream<Map.Entry<String, Integer>> stream2 = hm.entrySet().stream();
stream2.forEach(s -> System.out.println(s));
}
}
2.3 数组获取 stream 流
数组获取 stream 流 需要借助 Arrays 工具类的静态方法
代码:
package com.example.demo.Test;
import java.util.Arrays;
public class StreamDome3 {
public static void main(String[] args) {
// 1. 创建数组
int[] arr = {1, 2, 4, 5, 6, 7, 8, 9, 10};
// 2. 获取 stream 流
Arrays.stream(arr).forEach(s -> System.out.println(s));
System.out.println("------------------------------------");
// 3. 引用类型 获取 stream 流
String[] str = {"你好", "帅哥", "和", "美女", "点个赞"};
// 链式法则 打印出 str 里面的数据
Arrays.stream(str).forEach(s -> System.out.println(s));
Arrays.s
}
}
2.4 一堆零散数据获取 Stream 流
注意: 这一堆零散的数据需要是同一种数据类型 ,使用 Stream.of() 方法创建一个Stream 流
代码:
package com.example.demo.Test;
import java.util.stream.Stream;
public class StreamDome4 {
public static void main(String[] args) {
// 基础数据类型
Stream.of(1, 2, 3, 4, 5, 6).forEach(s -> System.out.println(s));
// 引用数据类型
Stream.of("你好", "帅哥", "点赞").forEach(s -> System.out.println(s));
}
}
效果:
小细节 : 这里 Stream.of() 创建的 Stream 可以对 引用类型的数组 创建
获取 Stream 流 看完,接下来来看看 有关于 Stream 的中间方法
3. Stream 流的中间方法
引用:
注意:
- 中间方法 , 返回 新的 Stream 流 ,原来的Stream 流只能使用一次 ,建议使用 链式编程
- 修改 Stream流中的 数据 , 不会影响原来集合或者数组中的数据
3.1 filter 过滤方法
图一:
图二:
代码:
package com.example.demo.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class StreamDome5 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
// 过滤规则 : 把张开头的留下来 , 其余的数据过滤不要
// list.stream().filter(new Predicate<String>() {
// @Override
// public boolean test(String s) {
// // 返回值为 true 表示当前数据留下 , 为 false 表示当前数据丢弃
// if (s.startsWith("张")) {
// // startsWith 方法 判断 s 是否以 张开头 如果是 返回 true 否者返回 false
// return true;
// }
// return false;
// }
// }).forEach(s -> System.out.println(s));
// list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));
// Stream<String> stream1 = list.stream().filter(s -> s.startsWith("张"));
//
// Stream<String> stream2 = stream1.filter(s -> s.length() == 3);
//
// stream2.forEach(s -> System.out.println(s));
// Stream<String> stream3 = stream1.filter(s -> s.length() == 3);
list.stream().
filter(s -> s.startsWith("张")).
filter(s -> s.length() == 3).
forEach(s -> System.out.println(s));
System.out.println(list);
}
}
3.2 limit 和 skip 方法
limit 方法 是用来获取 stream 流上前几个 元素的方法
skip 方法 是用来跳过 stream流 前指定元素个数的方法
代码:
package com.example.demo.Test;
import java.util.ArrayList;
import java.util.Collections;
public class StreamDome6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
// 通过 limit(3) 获得前 三个元素
list.stream().limit(3).forEach(s -> System.out.println(s));
System.out.println("------------------------");
// 通过 skip(4) 跳过前4 个 元素 , 打印 stream 流后面的元素
list.stream().skip(4).forEach(s -> System.out.println(s));
}
}
效果:
小练习
集合数据 : “张无忌”, “张三”, “赵敏”, “张强”, “张三丰”, “张翠山”, “张良”, “王二麻子”, “谢广坤”
这里想要打印出 张强 , 张三丰 , 张翠山
通过 limit 和 skip 方法 要如何做 才能 打印 .
答案 :
- 先通过 limit 获取到前6条数据 ,然后通过 skip 跳过前3 条数据即可
- 先通过 skip 跳过前3 条数据 ,然后通过 limit 获取 3 个数据
代码:
public class StreamDome6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
// 方法一:
list.stream().limit(6).skip(3).forEach(s -> System.out.println(s));
System.out.println("-------------------------");
// 方法二:
list.stream().skip(3).limit(3).forEach(s -> System.out.println(s));
}
}
效果:
3.3 distinct 去重方法
distinct 方法 实现元素曲中 是依赖 hashCode 和 equals 方法
public class StreamDome6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张三", "张三", "李四", "李四", "王五", "王五");
list.stream().distinct().forEach(s -> System.out.println(s));
}
}
效果: 可以看到通过 distinct 方法就将重复的数据去除了
这里 ArrayList 集合是已经实现好了 hashCode 和 equals 方法 所以能够 实现 元素去重 .
下面我们来简单的看看 distinct 方法是如何实现的 ,这里通过 ctrl + alt + b
快捷键 进入到 distinct 方法的实现类.
3.4 cancat 方法
cancat 方法 是用来将多个流合并成一个 流的方法
代码:
public class StreamDome6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
ArrayList<Object> list2 = new ArrayList<>();
Collections.addAll(list2, "喜羊羊", "灰太狼");
Stream.concat(list.stream(), list2.stream()).forEach(s -> System.out.println(s));
}
}
效果:
两个流的数据就合并到一起并通过 forEach 打印出来了.
3.5 map 方法
map 方法是用来 转化 流中的数据类型
代码: 匿名内部类
public class StreamDome6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-20", "张三-14", "赵敏-100");
// 需求: 只获取里面的年龄并进行打印 String --> int
// 第一个参数类型: 流中原本的数据类型
// 第二个参数类型: 要转成之后的类型
list.stream().map(new Function<String, Integer>() {
// apply 形参的 s: 依次表示流里面的每一个数据
// 返回值: 表示转换之后的数据
@Override
public Integer apply(String s) {
String[] arr = s.split("-");
String ageString = arr[1];
return Integer.parseInt(ageString);
}
// 当 map 方法执行完成后 , 流上的数据就变成了 整数
// 所以 在下面的 forEach 当中 , s 依次表示流里面的每个数据就是一个整数
}).forEach(s -> System.out.println(s));
}
}
Lambda 表达式
public class StreamDome6 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-20", "张三-14", "赵敏-100");
list.stream().map(s -> Integer.parseInt(s.split("-")[1])).forEach(s -> System.out.println(s));
}
}
4. Stream 流的终结方法
引用 :
4.1 forEach 遍历方法
forEach 上面一直在使用 ,就是依次遍历 流里面的每个数据,下面简单演示一下 就去看下一个方法
图:
代码:
package com.example.demo.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Consumer;
public class StreamDome7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
// 获取 stream 流
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
System.out.println("-----------");
list.stream().forEach(s -> System.out.println(s));
}
}
4.2 count 统计方法
count 方法 是用来统计 流里面数据的个数的 ,很简单 使用一边就会了.
代码:
public class StreamDome7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
System.out.println(list.size());
System.out.println("-------------------");
long number = list.stream().filter(s -> s.startsWith("张")).count();
System.out.println(number);
}
}
4.3 toArray 收集方法 (数组)
toArray 方法 是用来收集的 是将流里面的数据收集起来放到一个数组当中
图一:
图二:
代码:
public class StreamDome7 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
String[] array = list.stream().toArray(value -> {
return new String[value];
});
System.out.println(Arrays.toString(array));
}
public static void main5(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "张三", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");
String[] array = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
System.out.println(Arrays.toString(array));
}
}
4.4 Collect 收集方法 (集合)
Collect 方法是将 流上的数据 收集到 集合当中
图一:
图二:
代码:
public class StreamDome7 {
// Lambda 表达式 收集到 map 集合
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "张三-男-25", "赵敏-女-18", "张强-男-45", "张三丰-男-15",
"张翠山-男-15", "张良-男-58", "王二麻子-女-82", "谢广坤-女-68");
Map<String, Integer> map = list.stream().collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[2])
));
System.out.println(map);
}
// 收集到 Map 集合
public static void main8(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "张三-男-25", "赵敏-女-18", "张强-男-45", "张三丰-男-15",
"张翠山-男-15", "张良-男-58", "王二麻子-女-82", "谢广坤-女-68", "谢广坤-女-68", "谢广坤-女-68");
Map<String, Integer> map = list.stream().filter(s -> "女".equals(s.split("-")[1])).
collect(Collectors.toMap(
new Function<String, String>() {
@Override
public String apply(String s) {
String[] arr = s.split("-");
return arr[0];
}
},
new Function<String, Integer>() {
@Override
public Integer apply(String s) {
String[] arr = s.split("-");
return Integer.parseInt(arr[2]);
}
}
));
System.out.println(map);
}
// 收集到 Set 集合
public static void main7(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "张三-男-25", "赵敏-女-18", "张强-男-45", "张三丰-男-15",
"张翠山-男-15", "张良-男-58", "王二麻子-女-82", "谢广坤-女-68", "谢广坤-女-68", "谢广坤-女-68");
// 要求:
// 将 性别为男的数据 收集 Set 集合当中
Set<String> arr = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toSet());
System.out.println(arr);
}
// 收集到 List 集合
public static void main6(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "张三-男-25", "赵敏-女-18", "张强-男-45", "张三丰-男-15",
"张翠山-男-15", "张良-男-58", "王二麻子-女-82", "谢广坤-女-68", "谢广坤-女-68", "谢广坤-女-68");
// 要求:
// 将 性别为男的数据 收集 List 集合当中
List<String> arr = list.stream().filter(s -> "男".equals(s.split("-")[1])).collect(Collectors.toList());
System.out.println(arr);
}
}
到此 关于 stream 流的相关操作 就学习完成了下面来完成几个练习就结束本文的学习 .
5. Stream 流练习
5.1 数据过滤
题目一: 定义一个集合 , 并添加 一些整数 1,2,3,4,5,6,7,8,9,10
过滤偶数,只留下奇数 ,并将结果保存起来.
代码:
package com.example.demo.Test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collector;
import java.util.stream.Collectors;
public class StreamDemo8 {
public static void main(String[] args) {
ArrayList<Integer> arr = new ArrayList<>();
Collections.addAll(arr, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
List<Integer> arr2 = arr.stream().filter(number -> number % 2 == 1).collect(Collectors.toList());
System.out.println(arr2);
}
}
效果:
题目二 : 创建一个 ArrayList 集合 并添加一下字符 ,字符串前面是姓名 ,后面是 年龄 zhangsan,23
, lisi,24
, wangwu,25
保留大于等于 24 岁的 人 并将结果收集到 map 集合中 , 姓名为键 , 年龄为值。
代码:
public class StreamDemo8 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "zhangsan,23", "lisi,24", "wangwu,25");
Map<String, String> map = list.stream().filter(s -> 23 < Integer.parseInt(s.split(",")[1])).collect(Collectors.toMap(
s -> s.split(",")[0],
s -> s.split(",")[1]
));
System.out.println(map);
}
}
效果:
5.2 自定义对象过滤并收集
题目:
现在有两个 ArrayList 集合 , 分别存储 6 名 男演员 的名字 和 年龄 以及 6 名 女演员的名字和年龄 .
姓名 和 年ing 使用逗号隔开 , 比如 :张三,23
要求完成如下操作 :
- 男演员只要名字为 3 个字的前两人.
- 女演员只要姓杨的 , 并且不要第一个.
- 把过滤后的男演员姓名和女演员的姓名合并到一起
- 将上一步的演员姓名封装成 Actor 对象
- 将所有的演员都保存到 List 集合中
备注 : 演员类 Actor 属性有 : name , age
数据 :
男演员 : “菜坤坤 , 24” , “叶齁咸,23” , “刘不甜 , 22” , “吴签, 24” , “谷嘉,30” , “肖侯 , 27”
女演员 : “赵小颖,17”, “高圆圆,32”, “张天天,20”, “杨颖,20”,“刘亦菲,18”, “杨洋羊,20”
代码:
public class StreamDemo8 {
static class Actor {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Actor{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
public Actor(String name, Integer age) {
this.name = name;
this.age = age;
}
}
public static void main(String[] args) {
// 创建集合
ArrayList<String> manList = new ArrayList<>();
ArrayList<String> wuManList = new ArrayList<>();
// 添加数据
Collections.addAll(manList, "菜坤坤,24", "叶齁咸,23", "刘不甜,22", "吴签,24", "谷嘉,30", "肖侯,27");
Collections.addAll(wuManList, "赵小颖,17", "高圆圆,32", "张天天,20", "杨颖,20", "刘亦菲,18", "杨洋羊,20");
// 1. 男演员只要名字为 3 个字的前两人.
Stream<String> manStream = manList.stream().
filter(s -> s.split(",")[0].length() == 3).
limit(2);
// 2. 女演员只要姓杨的 , 并且不要第一个.
Stream<String> wuManStream = wuManList.stream().filter(s -> s.startsWith("杨")).skip(1);
// 3. 把过滤后的男演员姓名和女演员的姓名合并到一起 (合并两个流)
// Stream.concat(manStream, wuManStream).forEach(s -> System.out.println(s));
// 4. 将上一步的演员姓名封装成 Actor 对象 (将 String 转化为 Actor )
// 使用 map 方法 将 流里面的数据 进行类型转换
// 使用匿名内部类
// Stream.concat(manStream, wuManStream).map(new Function<String, Actor>() {
// @Override
// public Actor apply(String s) {
// String[] arr = s.split(",");
// String name = arr[0];
// Integer age = Integer.parseInt(arr[1]);
// Actor actor = new Actor();
// actor.setName(name);
// actor.setAge(age);
// return actor;
// }
// }).forEach(s-> System.out.println(s));
// 使用 Lambda
// Stream.concat(manStream, wuManStream).
// map(s -> {
// return new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1]));
// })
// .forEach(s -> System.out.println(s));
// 5. 将所有的演员都保存到 List 集合中
List<Actor> actors = Stream.concat(manStream, wuManStream).
map(s -> {
return new Actor(s.split(",")[0], Integer.parseInt(s.split(",")[1]));
})
.collect(Collectors.toList());
System.out.println(actors);
}
}
到此 本文 就完了, 关于 stream 流 其实还有一些 方法 可以自行了解 .