Stream流
Stream流
Stream流体验
1 |
|
Stream流思想
作用
结合Lambda表达式,简化集合、数组操作。
Stream流的使用步骤
先得到一条Stream流(流水线),并把数据放上去
获取方式 方式名 说明 单列集合 default Stream stream() Collection中的默认方法 双列集合 无 无法直接使用Stream流。通过keySet或entrySet再去使用Stream流 数组 public static Stream stream(T[] array) Arrays工具类中的静态方法 一堆零散数据 public static Stream of(T…values) Stream接口中的静态方法 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65package com.zhuixun.demo2;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* @Author: zhuixun
* @Date: 2023/2/13 22:11
* @Version 1.0
*/
public class StreamDemo2 {
public static void main(String[] args) {
//1.单列集合获取Stream流
System.out.println("==========单列集合获取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);
}
});
//Lambda表达式
//stream.forEach(s-> System.out.println(s));
//双列集合Stream
System.out.println("==========双列集合获取Stream流==============");
HashMap<String, Integer> hm = new HashMap<>();
//添加数据
hm.put("aaa",111);
hm.put("bbb",222);
hm.put("ccc",333);
hm.put("ddd",444);
//获取stream流
hm.keySet().stream().forEach(s -> System.out.println(s));
//第二种获取双列集合Stream
hm.entrySet().forEach(entry-> System.out.println(entry.getKey()+":"+entry.getValue()));
//数组
System.out.println("==========数组获取Stream流==============");
int[] arr = {1,2,3,4,5,6,7,8,9,10};
String[] arr2 ={"a","b","c"};
Arrays.stream(arr).forEach(s-> System.out.println(s));
Arrays.stream(arr2).forEach(s-> System.out.println(s));
System.out.println("==========一堆零散数据================");
//方法的形参是一个可变参数,可以传递一堆零散数据,也可以传递数组,但是数组必须是引用数据类型的
//如果传递基本数据类型,是会把整个数组当做一个元素,放到Stream当中
Stream.of(1,2,3,4,5,6).forEach(s-> System.out.println(s));
}
}利用Stream流中的API进行各种操作(比如过滤、转换、统计、打印等等)
中间方法(方法调用完毕之后,还可以调用其他方法,比如过滤、转换)
名称 说明 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) 转换流中的数据类型 注意1:中间方法,返回新的Stream流,原来的Stream流只能使用一次,建议使用链式编程
注意2:修改Stream流中的数据,不会影响原来集合或者数组中的数据
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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78package com.zhuixun.demo2;
import org.w3c.dom.ls.LSOutput;
import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
/**
* @Author: zhuixun
* @Date: 2023/2/13 22:48
* @Version 1.0
*/
public class StreamDemo3 {
public static void main(String[] args) {
/**
* filter:过滤
* limit:获取前几个元素
* skip:跳过前几个元素
*/
ArrayList<String> list = new ArrayList<>();
ArrayList<String> list2 = new ArrayList<>();
Collections.addAll(list,"张无忌","张无忌","赵敏","张强","张三丰",
"张翠山","张无忌","王二麻子","谢广坤");
Collections.addAll(list2,"小昭","韦一笑","杨逍");
//过滤
list.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
//如果返回值为true,表示当前数据留下,如果返回值false,表示当前舍弃不要
return s.startsWith("张");
}
}).forEach(s-> System.out.println(s));
System.out.println("===========Lambda表达式==================");
//Lambda表达式
list.stream().filter(s->s.startsWith("张")).forEach(s-> System.out.println(s));
System.out.println("===============limit================");
//获取数组前三个元素
list.stream().limit(3).forEach(s-> System.out.println(s));
//跳过前4个元素 skip
System.out.println("===============skip================");
list.stream().skip(4).forEach(s-> System.out.println(s));
System.out.println("===============distinct================");
//去重
list.stream().distinct().forEach(s-> System.out.println(s));
System.out.println("===========concat==================");
//拼接多个list
Stream.concat(list.stream(),list2.stream()).forEach(s-> System.out.println(s));
System.out.println("===========map==================");
//转换流中的数据
ArrayList<String> list3 = new ArrayList<>();
Collections.addAll(list3,"张无忌-15","赵敏-14","张强-13","张三丰-100",
"张翠山-15","王二麻子-34","谢广坤-51");
//需求:只获取里面的年龄并进行打印
//第一个类型:流中原本的数据类型 第二个数据类型:要转成之后的类型
//apply的形参s:依次表示流里面的每一个数据 返回值:表示转换之后的数据类型
//当map方法执行完毕,流上的数据变成了整数,所以在下面forEach当中,s依次表示流里面的每一个数据,这个数据就是整数
list3.stream().map(new Function<String, Integer>() {
@Override
public Integer apply(String s) {
String[] split = s.split("-");
String ageString = split[1];
int age = Integer.parseInt(ageString);
return age;
}
}).forEach(s-> System.out.println(s));
System.out.println("=============链式编程===================");
list3.stream().map(s->Integer.parseInt(s.split("-")[1])).forEach(s-> System.out.println(s));
}
}终结方法(最后一步,调用完毕之后,不能调用其他方法,比如:统计,打印)
名称 说明 void forEach(Consumer action) 遍历 long count() 统计 toArray() 收集流中的数据,放到数组中 collect(Collector collector) 收集流中的数据,方法集合中 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115package com.zhuixun.demo2;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
/**
* @Author: zhuixun
* @Date: 2023/2/15 20:17
* @Version 1.0
*/
public class StreamDemo4 {
public static void main(String[] args) {
/**
* void forEach(Consumer action) 遍历
* long count() 统计
* toArray() 收集流中的数据,放到数组中
*/
ArrayList<String> list = new ArrayList<>();
Collections.addAll(list,"张无忌","周知罗","赵敏","张强","张三丰","张翠三","王二麻子","张良","谢广坤");
System.out.println("=============遍历===============");
list.stream().forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
//forEach方法返回的void,所以该方法结束之后不能调用其他方法
list.stream().forEach(s-> System.out.println("Lambda"+s));
System.out.println("================统计====================");
long count = list.stream().count();
System.out.println("个数:"+count);
System.out.println("================放到数组中===================");
Object[] arr1 = list.stream().toArray();
System.out.println(Arrays.toString(arr1));
//IntFunction的泛型:具体类型的数组
//apply的形参:流中数据的个数,要跟数组的长度保持一致
//apply的返回值:具体类型的数组
//方法体:就是创建数组
//toArray方法的参数的作用:负责创建一个指定类型的数组
//toArray方法的底层,会依次得到流里面的每一个数据,并把数据放到数组中
//toArray方法的返回值,是一个装着流里面所有数据的数组
String[] arr = list.stream().toArray(new IntFunction<String[]>() {
@Override
public String[] apply(int value) {
return new String[value];
}
});
System.out.println(Arrays.toString(arr));
String[] strings = list.stream().toArray(value -> new String[value]);
System.out.println(Arrays.toString(strings));
System.out.println("===============collect===============");
ArrayList<String> collectList = new ArrayList<>();
Collections.addAll(collectList,"张无忌-男-23","周智络-女-12","赵敏-女-15","张强-男-20",
"张三丰-男-100","张翠山-男-40","张良-男-35","王二麻子-男-39","谢广坤-男-41");
//收集List集合当中 需求:把所有的男性收集起来
List<String> newList = collectList.stream().filter(s -> s.split("-")[1].equals("男")).collect(Collectors.toList());
System.out.println(newList);
//收集Set集合当中
Set<String> newSet2 = collectList.stream().filter(s -> s.split("-")[1].equals("男")).collect(Collectors.toSet());
System.out.println(newSet2);
//收集Map集合当中
/**
* toMap:参数1表示键的生成规则
* 参数2表示值的生成规则
*
* 参数1:
* Function泛型一:表示流中每一个数据类型
* 泛型二:表示Map集合中键的数据类型
* 方法apply形参:依次表示流里面的每一个数据
* 方法体:生成键的代码
* 返回值:已经生成的键
*参数1:
* Function泛型一:表示流中每一个数据类型
* 泛型二:表示Map集合中值的数据类型
* 方法apply形参:依次表示流里面的每一个数据
* 方法体:生成值的代码
* 返回值:已经生成的值
*
*/
Map<String, Integer> map = collectList.stream().filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(
new Function<String, String>() {
@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("===========Lambda表达式===================");
collectList.stream().filter(s -> "男".equals(s.split("-")[1]))
.collect(Collectors.toMap(s->s.split("-")[0],s->Integer.parseInt(s.split("-")[2])));
map.entrySet().stream().forEach((Map.Entry<String, Integer> stringIntegerEntry)-> {
System.out.println("键:"+stringIntegerEntry.getKey()+" 值:"+stringIntegerEntry.getValue());
}
);
}
}
练习
1 |
|
不可变集合
简单理解:不想让被别人修改集合中的内容
- 如果某个数据不能被修改,把它防御型的拷贝到不可变集合中是个很好的实践
- 或者当集合对象被不可信的库调用时,不可变形式是安全的。
创建不可变集合的书写格式
在List、Set、Map接口中,都存在静态的of方法,可以获取一个不可变的集合
方法名称 | 说明 |
---|---|
static | 创建一个具有指定元素的List集合对象 |
static | 创建一个具有指定元素的Set集合对象(放入里面的值不能重复) |
static<K,V> Map<K,V> of(E…elements) | 创建一个具有指定元素的Map集合对象(参数上限20,只能存储10个键值对) |
static<K,V> Map<K,V> ofEntries (Entry<? extends K,? extends V> … entries) | 创建一个具有指定元素的Map集合对象(参数上限大于20,因为可变集合要放在参数的最后面,并且方法参数中只能有一个不可变参数,所以把K,V看做一个整体去做一个可变集合) |
注意:这个集合不能添加,不能删除、不能修改
1 |
|
1 |
|
总结
不可变集合的特点
定义完成后不可以修改,或者添加、删除
如何创建不可变集合
List、Set、Map接口中,都存在of方法可以创建不可变集合
三种方式的细节
- List:直接用
- Set: 元素不能重复
- Map:元素不能重复、键值对数量最多是10个,超过10个用ofEntries方法
方法引用
把已经有的方法拿过来用,当做函数式接口中抽象方法的方法体
1.引用出必须是函数式接口
2.被引用的方法必须已经存在
3.被引用方法的形参和返回值需要跟抽象方法保持一致
4.被引用方法的功能要满足当前需求
1 |
|
方法引用的分类
引用静态方法
格式:类名::静态方法
范例:Integer::parseInt
1 |
|
引用成员方法
格式:对象::成员方法
- 其他类:其他类对象::方法名
- 本类: this::方法名(不能写在静态方法当中)
- 父类:super::方法名(不能写在静态方法当中)
1 |
|
引用构造方法
格式:类名::new
范例:Student::new
1 |
|
其他调用方式
使用类名引用成员方法
格式:类名::成员方法
范例:String::substring
局限性:不能引用所有类中的成员方法,跟抽象方法的第一个参数有关(第一个参数就是下面代码中的String s),这个参数是什么类型的,那么就只能引用这个类中的方法
方法引用的规则:
- 需要有函数式接口
- 被引用的方法必须已经存在
- 被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致
- 被引用方法的功能需要满足当前需求
抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用那些类中的方法
在Stream流中,第一个参数一般都表示流里面的每一个数据,假设流里面的数据 都是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法
第二个参数到最后一个参数:
跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要的 无参的成员方法
1 |
|
引用数组的构造方法
格式:数据类型[]::new
范例:int[]::new
细节:数组的类型,需要跟流中数据的类型保持一致
1 |
|