1. Stream流
数据流
- 用来对集合数据做处理的
1.1. 案例需求
按照下面的要求完成集合的创建和遍历
- 创建一个集合,存储多个字符串元素
- 把集合中所有以"张"开头的元素存储到一个新的集合
- 把"张"开头的集合中的长度为3的元素存储到一个新的集合
- 遍历上一步得到的集合
1.2. 原始方式示例代码
ArrayList<String> list1 = new ArrayList<>();
Collections.addAll(list1,"张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤");
//遍历list1把以张开头的元素添加到list2中。
ArrayList<String> list2 = new ArrayList<>();
for (String s : list1) {
if(s.startsWith("张")){
list2.add(s);
}
}
//遍历list2集合,把其中长度为3的元素,再添加到list3中。
ArrayList<String> list3 = new ArrayList<>();
for (String s : list2) {
if(s.length() == 3){
list3.add(s);
}
}
for (String s : list3) {
System.out.println(s);
}
1.3. 流的实现方式
ArrayList<String> list1 = new ArrayList<>(List.of("张三丰", "张无忌", "张翠山", "王二麻子", "张良", "谢广坤"));
//1 获取流 list1.stream()
//2 过滤 姓张的数据 filter(s -> s.startsWith("张"))
//3 过滤 长度为3的数据 filter(s -> s.length() == 3)
//4 打印 终结操作 forEach(s -> System.out.println(s))
list1.stream().filter(s -> s.startsWith("张")).
filter(s -> s.length() == 3).
forEach(s -> System.out.println(s));
1.3.1. Stream流的思想
- 类似流水线,在过程中可以多次处理流数据
1.3.2. Stream流的三类方法
- 1获取Stream流
- 创建一条流水线,并把数据放到流水线上准备进行操作
- 2中间方法
- 流水线上的操作
- 一次操作完毕之后,还可以继续进行其他操作
- 3终结方法
- 一个Stream流只能有一个终结方法
- 是流水线上的最后一个操作
1.3.3. 生成Stream流的方式
- 1 Collection体系集合(List Set)
- 使用默认方法stream()生成流
- 2 Map体系集合
- 把Map转成Set集合(keySet,entrySet),间接的生成流
- keySet().stream() 或者 entrySet().stream()
- 3 数组
- 通过Arrays中的静态方法stream生成流 Arrays.stream(arr)
- 4 同种数据类型的多个数据
- 通过Stream接口的静态方法of(T... values)生成流 , 原理还是数组变流
1.3.4. 代码演示
//1Collection体系集合 都用stream方法
Collection<Integer> coll = new TreeSet<>();
Stream<Integer> stream = coll.stream();
//2 Map
Map<String ,String> map = new HashMap<>();
//获取所有的key的set集合 再用set集合获取流
Set<String> keys = map.keySet();
Stream<String> stream1 = keys.stream();
//获取所有的键值对的的set集合 再用set集合获取流
Set<Map.Entry<String, String>> entries = map.entrySet();
Stream<Map.Entry<String, String>> stream2 = entries.stream();
//3 数组
int[] arr = new int[]{3,4,5,7,8};
//用Arrays工具类获取流对象
IntStream stream3 = Arrays.stream(arr);
//4 既然数组可以, 可变参数本质是数组
//Stream.of方法就是可以把同类型的多个数据转为流对象, 内部就是数组转流
Stream<String> stream4 = Stream.of("aaa", "bbb", "ccc");
1.4. 中间操作方法
1.4.1. 概念
- 中间操作的意思是,执行完此方法之后,Stream流依然可以继续执行其他操作
1.4.2. 常见方法
方法名 | 说明 |
---|---|
Stream\ |
用于对流中的数据进行过滤 |
Stream\ |
返回流中最前面 指定参数个数的数据组成的流 |
Stream\ |
跳过指定参数个数的数据,返回由该流的剩余元素组成的流 |
static \ |
合并a和b两个流为一个流 |
Stream\ |
返回 去掉流数据中 重复的元素后剩余数据组成的流 |
1.4.3. filter代码
- 流的操作不影响原集合的数据
ArrayList<String> al = new ArrayList<String>();
Collections.addAll(al, "周润发", "成龙", "小明", "刘德华", "吴京", "周星驰", "李连杰");
//流的操作不影响原集合的数据
al.stream().filter(s -> s.length() >= 3).forEach(s -> System.out.println(s));
//会把流数据里的每一条数据 传到test方法中 获取返回值,返回为true的数据留下,为false的删除
al.stream().filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.length() == 3;
}
});
1.4.4. limit skip代码
ArrayList<String> al = new ArrayList<String>(
List.of("周润发", "成龙", "小明", "刘德华", "吴京", "周星驰", "李连杰"));
//limit从前面获取指定数量的数据
al.stream().limit(5).forEach(s -> System.out.println(s));
System.out.println("----------------------------------");
//跳过前3个 获取后面的所有数据
al.stream().skip(3).forEach(s -> System.out.println(s));
System.out.println("----------------------------------");
//获取前5个 然后跳过前3个
al.stream().limit(5).skip(3).forEach(s -> System.out.println(s));
1.4.5. concat 和distinct
// static <T> Stream<T> concat(Stream a, Stream b) 合并a和b两个流为一个流
ArrayList<String> al1 = new ArrayList<String>();
Collections.addAll(al1, "林心如", "张曼玉", "林青霞", "柳岩", "林志玲", "小明", "王祖贤");
Stream.concat(al.stream(), al1.stream()).forEach(s -> System.out.println(s));
System.out.println("--------------------------------------------------------------");
// Stream<T> distinct() 返回 去掉流数据中 重复的元素后剩余数据组成的流
Stream.concat(al.stream(), al1.stream()).distinct().forEach(s -> System.out.println(s));
1.5. 终结操作方法
1.5.1. 概念
- 终结操作的意思是,执行完此方法之后,Stream流将不能再执行其他操作
1.5.2. 常见方法
方法名 | 说明 |
---|---|
void forEach(Consumer action) | 对此流的每个元素执行操作 |
long count() | 返回此流中的元素数 |
1.5.3. 代码
ArrayList<String> al = new ArrayList<String>();
Collections.addAll(al,"周润发", "成龙", "小明", "刘德华", "吴京", "周星驰", "李连杰")
al.stream().skip(2).limit(2).forEach(s -> {
System.out.println(s);
});
long count = al.stream().skip(2).count();//获取数据的数量
System.out.println(count);
1.5.4. collect收集操作
- collect方法 获取流中剩余的数据,但是他不负责创建容器,也不负责把数据添加到容器中.
- Collectors提供了具体的收集方式
- 收集到List 、收集到Set、收集到Map
方法名 | 说明 |
---|---|
public static \ |
把元素收集到List集合中 |
public static \ |
把元素收集到Set集合中 |
public static Collector toMap(Function keyMapper,Function valueMapper) | 把元素收集到Map集合中 |
1.5.5. 收集到list和set的代码
- 把集合转为流,然后把偶数留下,收集到新的集合中(list或者set)返回
public static void main(String[] args) {
ArrayList<Integer> al = new ArrayList<>();
for (int i = 1; i <= 10; i++) {
al.add(i);
}
//添加一些重复数据 为了后面set集合演示去重复
al.add(10);
al.add(10);
al.add(10);
al.add(10);
//使用流 获取所有的偶数 最后collect收集到List集合里
List<Integer> list = al.stream().filter(number -> number % 2 == 0).collect(Collectors.toList());
System.out.println(list);
//使用流 获取所有的偶数 最后collect收集到set集合中返回,这里返回的是set集合,重复的数据最后会默认去除
Set<Integer> set = al.stream().filter(number -> number % 2 == 0).collect(Collectors.toSet());
System.out.println(set);
}
1.5.6. 收集到map
- 把学生的名字作为key , 年龄作为value
package com.heima.test5;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
public class Demo05 {
public static void main(String[] args) {
ArrayList<String> al = new ArrayList<String>(List.of("001,周润发", "002,成龙", "003,小明", "004,刘德华", "005,吴京"));
//把集合的数据 存到map里 序号作为key 人名作为值 {"001":"周润发","002":"成龙"}
Map<String, String> map = al.stream().collect(Collectors.toMap(
new Function<String, String>() {
@Override
public String apply(String s) {
//返回值作为key 一般会通过处理s得到
return s.split(",")[0];
}
}, new Function<String, String>() {
@Override
public String apply(String s) {
//返回值value 一般会通过处理s得到
return s.split(",")[1];
}
}
));
//lambda写法
Map<String, String> map = al.stream().collect(
Collectors.toMap(
//返回key "001,周润发" 逗号分割 第0个数据返回作为key
s -> s.split(",")[0],
///返回value "001,周润发" 逗号分割 第1个数据返回作为value
s -> s.split(",")[1]
)
);
System.out.println(map);
}
}
1.6. 综合案例
1.6.1. 案例需求
- 现在有两个ArrayList集合,分别存储6名男演员名称和6名女演员名称,要求完成如下的操作
- 男演员只要名字为3个字的前三人
- 女演员只要姓林的,并且不要第一个
- 把过滤后的男演员姓名和女演员姓名合并到一起
- 把上一步操作后的元素作为构造方法的参数创建演员对象,遍历数据
- 演员类Actor已经提供,里面有一个成员变量,一个带参构造方法,以及成员变量对应的get/set方法
1.6.2. 演员类代码
public class Actor {
private String name;
public Actor(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1.6.3. 测试类代码
public static void main(String[] args) {
ArrayList<String> manList = new ArrayList<String>();
Collections.addAll(manList, "周润发", "成龙", "刘德华", "吴京", "周星驰", "李连杰");
ArrayList<String> womanList = new ArrayList<String>();
Collections.addAll(womanList, "林心如", "张曼玉", "林青霞", "柳岩", "林志玲", "王祖贤");
//名字为3个字的前三人
Stream<String> manStream = manList.stream().filter(s -> s.length() == 3).limit(3);
//只要姓林的,并且不要第一个
Stream<String> womanStream = womanList.stream().filter(s -> s.startsWith("林")).skip(1);
//合并过滤后的男演员和女演员
Stream<String> newStream = Stream.concat(manStream, womanStream);
newStream.forEach(s -> {
//创建演员对象 把名字传入
Actor actor = new Actor(s);
//获取演员的对象的名字打印
System.out.println(actor.getName());
});
}
1.7. flatMap
- map 对管道流中的每一个数据元素进行转换操作。
ArrayList<String> list = new ArrayList<>();
list.add("zhangsan,15");
list.add("lisi,18");
list.add("wangwu,26");
list.add("xiaoming,19");
//map中间操作 转换功能 把流中的数据 经过处理 转为另外一种类型的数据
list.stream().map(new Function<String, Student>() {
@Override //zhangsan,15
public Student apply(String s) {
//1 s用逗号切割
String[] ss = s.split(",");
//2 生成学生对象 注意年龄转为int
Student stu = new Student(ss[0], Integer.parseInt(ss[1]));
return stu;
}
}).forEach(s -> System.out.println(s));
- flatMap
public static void main(String[] args) {
ArrayList<Student> list1 = new ArrayList<>();
list1.add(new Student("zs", 20));
list1.add(new Student("lisi", 22));
list1.add(new Student("wangwu", 21));
ArrayList<Student> list2 = new ArrayList<>();
list2.add(new Student("xiaohong", 20));
list2.add(new Student("xiaolan", 19));
list2.add(new Student("xiaofang", 21));
ArrayList< ArrayList<Student> > list = new ArrayList<>();
//注意 list集合里装的是集合
list.add(list1);
list.add(list2);
System.out.println(list);
//[[zs--20, lisi--22, wangwu--21], [xiaohong--20, xiaolan--19, xiaofang--21]]
//把list的里的所有学生 全部放到一个流对象里面
//过滤所有学生 保留年龄大于20的
list.stream().flatMap(new Function< ArrayList<Student>, Stream<Student> >() {
@Override
public Stream<Student> apply(ArrayList<Student> students) {
return students.stream();
}
}).filter(stu -> stu.age > 20).forEach(stu -> System.out.println(stu));
}
-
flatMap和map的区别
map是把数据处理完 转为其他类型的对象
flatMap是把不同数据里的集合里的数据拿出来,合并放一个集合中
1.7.1. 案例
- 有user类 ,衣服类, 一个user对象 可以有多个衣服对象
import java.util.List;
//衣服
class Clothes {
String name;
public Clothes(String name) {
this.name = name;
}
}
//用户
public class User {
String name;
List<Clothes> cs;
public User(String name, List<Clothes> cs) {
this.name = name;
this.cs = cs;
}
}
- 初始数据
public static void main(String[] args) {
Clothes c3 = new Clothes("李宁羽绒服");
Clothes c1 = new Clothes("阿玛尼背心");
Clothes c2 = new Clothes("耐克运动裤");
Clothes c4 = new Clothes("李宁夹克");
Clothes c5 = new Clothes("阿迪短裤");
Clothes c6 = new Clothes("安踏篮球裤");
Clothes c7 = new Clothes("李宁帽子");
ArrayList<Clothes> list01 = new ArrayList<>();
ArrayList<Clothes> list02 = new ArrayList<>();
Collections.addAll(list01, c1, c2, c3, c4);
Collections.addAll(list02, c5, c6, c7);
User u01 = new User("zs", list01);
User u02 = new User("lisi", list02);
ArrayList<User> users = new ArrayList<>();
users.add(u01);
users.add(u02);
}
- 把所有用户的衣服合并到一个集合中
Comments NOTHING