Stream流

该笔记基于b站视频完成Java-Stream流从入门到精通

什么是Stream流

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据
Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象
Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码
这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等
元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果


Stream流的作用:结合了Lambda表达式,简化了集合、数组的操作

Stream流的使用步骤

  1. 先得到一条Stream流,并把数据放上去
  2. 利用Stream流中的API进行各种操作
    • 中间方法:过滤、转换 方法调用完毕之后、还可以调用其他方法
    • 终结方法:统计、打印 最后一步,调用完毕之后,不能再调用其他方法

把数据放到Stream流上去

获取方式 方法名 说明
单例集合 default Stream stream() Collection中的默认方法
双列集合 无法直接使用Stream流
数组 public static Stream stream (T… values) Arrays工具类中的静态方法
一堆零散的数据 public static Stream of (T… values) Stream接口中你的静态方法

单例集合获取Stream流

1
2
3
4
5
// 用Stream流遍历集合中的元素
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "a", "b", "b", "d");

list.stream().forEach(s -> System.out.println(s));

双列集合获取Stream流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 创建双列集合
HashMap<String, Integer> hm = new HashMap<>();
// 添加数据
hm.put("a",1);
hm.put("b",2);
hm.put("c",3);
hm.put("d",4);

// 第一中获取Stream流方法
hm.keySet().stream().forEach(s -> System.out.println(s));
hm.values().stream().forEach(x -> System.out.println(x));

// 第二种
hm.entrySet().stream().forEach(y -> System.out.println(y));

数组获取Stream流

1
2
int[] arr = {1,2,3,4,5};
Arrays.stream(arr).forEach(s -> System.out.println(s));

一堆零散数据获取Stream流

1
2
3
// 前提是这些数据的数据类型相同
Stream(1,2,3,4,5)..forEach(s -> System.out.println(s));
Stream("a","b","c")..forEach(s -> System.out.println(s));

Stream流的中间方法

名称 说明
Stream filter(Predicate<? super T> predicate) 过滤
Stream limit(long maxSize) 获取前面几个元素
Stream skip(long n) 跳过前面几个元素
Stream distinct() 元素去重,依赖于hashCode和equals方法
static Stream concat(Stream a, Stream b) 合并a和b两个流为一个流
Stream map(Function<T, R> mapper) 转换流中的数据类型

注意:

  • 中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
  • 修改Stream流中的数据,不会影响原来集合或者数据中的数据

filter、limit、skip

1
2
3
4
5
6
7
8
9
10
11
12
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");

// filter 过滤 把张开头的留下,其余的数据过滤
list.stream().filter(s -> s.startsWith("张")).forEach(s -> System.out.println(s));

// limit: 获取前面几个元素 skip: 跳过前面几个元素
// 获取 "张强", "张三丰", "张翠山"
// 实现方法一:先获取前面6个元素,再跳过前面3个元素
list.stream().limit(6).skip(3).forEach(s -> System.out.println(s));
// 实现方法二:先跳过前面3个,在获取前面3个
list.stream().skip(3).limit(3).forEach(s -> System.out.println(s));

distinct、concat

1
2
3
4
5
6
7
8
9
10
11
// distinct : 元素去重	concat : 合并两个流
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "a", "a", "a", "b", "b", "c", "d", "e", "f");

// 去重输出
list1.stream().distinct().forEach(s -> System.out.println(s));

// 合并两个流
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1, "g", "h");
Stream.concat(list1.stream(), list2.stream());

map

1
2
3
4
5
6
7
// map 转换流中的数据类型
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-15", "周芷若-18", "赵敏-17", "张强-31", "张三丰-23", "张翠山-15", "张良-20", "王二麻子-25", "谢广坤-40");
// 需求:只获取里面的年龄并进行打印
list.stream()
.map(s -> Integer.parseInt(s.split("-")[1]))
.forEach(s -> System.out.println(s));

Stream流的终结方法

名称 说明
void forEach(Consumer action) 遍历
long count() 统计
toArray() 收集流中的数据,放到数组中
collect(Collector collector) 收集流中的数据,放到集合中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰", "张翠山", "张良", "王二麻子", "谢广坤");

// 使用forEach遍历集合
list.stream().forEach(a -> System.out.println(s));

// 使用count统计个数
long count = list.stream().filter(s -> s.startsWith("张")).count();
System.out.println(count);

// 收集流流中的数据,放到数组中 toArray
Object[] arr1 = list.stream().toArray();
// 指定数组数据类型
String[] arr2 = list.stream().toArray(value -> new String[value]);

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list, "张无忌-男-15", "周芷若-女-18", "赵敏-女--17", "张强-男-31", "张三丰-男-23", "张翠山-女-15", "张良-男-20", "王二麻子-男-25", "谢广坤-男-40");

// 把所有的男性收集起来,收集到List集合中
List<String> newList = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toList());

// 把所有的男性收集起来,收集到Set集合中
List<String> newSet = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toSet());

// 区别:Set集合会去重

// 把所有的男性收集起来,收集到Map集合中,键:姓名,值:年龄
Map<String, Integer> newMap = list.stream()
.filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
s -> s.split("-")[0],
s -> Integer.parseInt(s.split("-")[3])
));
// toMap()中,第一个参数是键,第二个参数是值


总结

  1. Stream流的作用

    结合了Lambda表达式,简化集合、数组的操作

  2. Stream流的使用步骤

    • 获取Stream流对象
    • 使用中间方法处理数据
    • 使用终结方法处理数据
  3. 如何获取Stream流对象

    • 单列集合:Collection中的默认方法Stream
    • 双列集合:不能直接获取,要转为单列集合
    • 数组:Arrays工具类中的静态方法Stream
    • 零散的数据:Stream接口中的静态方法of
  4. 常见方法

    • 中间方法:filter、limit、skip、distinct、concat、map
    • 终结方法:forEach、count、collect

练习题

定义一个集合,并添加一些整数1,2,3,4,5,6,7,8,9,10
过滤奇数,只留下偶数
并将结果存下来

1
2
3
4
5
6
ArrayList<Integer> list = new ArrayList<>();
Collections.addAll(list,1,2,3,4,5,6,7,8,9,10);
List<Integer> collect = list.stream()
.filter(s -> s % 2 == 0)
.collect(Collectors.toList());
System.out.println(collect);

创建一个ArrayList集合,并添加一下字符串,字符串中前面是姓名,后面是年龄
“张三,23”
“李四,24”
“王五,25”
保留年龄大于等于24岁的人,并将结果收集到Map集合中,姓名为键,年龄为值

1
2
3
4
5
6
7
8
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张三,23","李四,24","王五,25");
Map<String, Integer> map = list.stream()
.filter(s -> Integer.parseInt((s.split(",")[1])) >= 24)
.collect(Collectors.toMap(
s -> (s.split(",")[0]),
s -> Integer.parseInt(s.split(",")[1])));
System.out.println(map);

现在有两个ArrayList集合,
第一个集合中:存储6名男演员的名字和年龄,第二个集合中:存储6名女演员的名字和年龄
姓名和年龄中间用逗号隔开。如:”张三,23”
要求完成如下的操作:

  1. 男演员只要名字为3个字的前两人
  2. 女演员只要姓杨的,并且不要第一个
  3. 把过滤后的男演员姓名和女演员姓名合并到一起
  4. 将上一步的演员信息封装成Actor对象
  5. 将所有的演员对象都保存到List集合中

演员类属性: name, age

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
ArrayList<String> manList = new ArrayList<>();
Collections.addAll(manList,"蔡坤坤,24","叶齁咸,23","刘不甜,22","吴签,24","谷嘉,30","肖凉凉,27");
ArrayList<String> womenList = new ArrayList<>();
Collections.addAll(womenList,"赵小颖,35","杨颖,36","高圆圆,43","张天天,31","刘诗,35","杨小幂,33");

// 男演员只要名字为3个字的前两人
Stream<String> menStream = manList.stream().filter(s -> s.split(",")[0].length() == 3).limit(2);
// 女演员只要姓杨的,并且不要第一个
Stream<String> womenStream = womenList.stream().filter(s -> s.split(",")[0].startsWith("杨")).skip(1);

// 把过滤后的男演员和女演员合并到一起
List<Actor> actorList = Stream.concat(menStream, womenStream)
.map(s -> new Actor(s.split(",")[0]
, Integer.parseInt(s.split(",")[1])))
.collect(Collectors.toList());
System.out.println(actorList);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// Actor类
public class Actor {
private String name;
private int age;

public Actor() {
}

public Actor(String name, int age) {
this.name = name;
this.age = age;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}

@Override
public String toString() {
return "Actor{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}


Stream流
https://lzhengjy.github.io/2023/10/24/Stream流/
作者
Zheng
发布于
2023年10月24日
许可协议