jdk8中出现了stream,lambda表达式等,对操作集合提供了很快捷的操作;
主要功能实现:将操作元素当成流来出来,然后调用stream API方法,对元素进行操作。
1)流的操作类型流的操作类型分为两种:
-
Intermediate(中间的):一个流可以后面跟随零个或多个intermediate操作。其目的主要是打开流,做出某种程度的数据映射/过滤,然后返回一个新的流,交给下一个操作使用。这类操作都是惰性化的(lazy),就是说,仅仅调用到这类方法,并没有真正开始流的遍历。
-
Terminal(终端):一个流只能有一个terminal操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以,这必定是流的最后一个操作。Terminal操作的执行,才会真正开始流的遍历,并且会生成一个结果,或者一个side effect。
在对一个Stream进行多次转换操作(Intermediate 操作),每次都对Stream的每个元素进行转换,而且是执行多次,这样时间复杂度就是N(转换次数)个for循环里把所有操作都做掉的总和吗?其实不是这样的,转换操作都是lazy的,多个转换操作只会在Terminal操作的时候融合起来,一次循环完成。我们可以这样简单的理解,Stream里有个操作函数的集合,每次转换操作就是把转换函数放入这个集合中,在Terminal 操作的时候循环Stream对应的集合,然后对每个元素执行所有的函数。
还有一种操作被称为short-circuiting。用以指:对于一个intermediate操作,如果它接受的是一个无限大(infinite/unbounded)的Stream,但返回一个有限的新Stream;对于一个terminal操作,如果它接受的是一个无限大的Stream,但能在有限的时间计算出结果。
当操作一个无限大的 Stream,而又希望在有限时间内完成操作,则在管道内拥有一个short-circuiting操作是必要非充分条件。
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
- stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
可以创建并行流和串行流
两者之间的区别详见:https://blog.csdn.net/weixin_43987718/article/details/124388021
并行流也可以通过:
集合创建流
ListstrList = Arrays.asList("a", "b", "c", "d"); // 串行流 List list = strList.stream().filter(x -> x.equals("a")).collect(Collectors.toList()); // 并行流创建的两种方式 List list2 = strList.parallelStream().filter(x -x.equals("a")).collect(Collectors.toList()); List list2 = strList.stream().parallel().filter(x -> x.equals("a")).collect(Collectors.toList());
也可以通过Arrays来创建
// 1. Individual values
Stream stream = Stream.of("a", "b", "c");
// 2. Arrays
String [] strArray = new String[] {"a", "b", "c"};
stream = Stream.of(strArray);
stream = Arrays.stream(strArray);
// 3. Collections
List list = Arrays.asList(strArray);
stream = list.stream();1234567891011
2、流的常用方法
1、match、foreach、find使用
创建一个实体
@Data
public class Person {
private String name;
private String sex;
private int age;
private int salary;
private String country;
Person(String name ,String sex,int age,int salary,String country){
this.salary = salary;
this.name = name;
this.sex = sex;
this.age = age;
this.country = country;
}
}
操作:
public class StreamDemo2 {
public static void main(String[] args) {
// 流的常用操作,筛选,匹配,遍历
List personList = new ArrayList<>();
personList.add(new Person("小张","男",18,3200,"南京"));
personList.add(new Person("小李","女",18,8200,"连云港"));
personList.add(new Person("小王","女",22,10200,"徐州"));
personList.add(new Person("小刘","男",24,8200,"苏州"));
personList.add(new Person("小梁","女",26,5200,"上海"));
personList.add(new Person("小六","男",19,9200,"无锡"));
// 遍历
personList.stream().forEach(p->{
System.out.println(p);
});
// 筛选
// 筛选大于20岁的第一个
Optional first = personList.stream().filter(p -> p.getAge() > 20).findFirst();
// findAny 常和并行流一起搭配使用
Optional firs2 = personList.stream().parallel().filter(p -> p.getAge() > 20).findAny();
// 是否存在大于20岁的
boolean b = personList.stream().anyMatch(p -> p.getAge() > 20);
System.out.println("筛选大于20岁的第一个"+ JSONObject.toJSONString(first));
System.out.println("筛选大于20岁的任意一个"+ JSONObject.toJSONString(firs2));
System.out.println("是否存在大于20岁的"+ b);
}
}
结果:
Person(name=小张, sex=男, age=18, salary=3200, country=南京)
Person(name=小李, sex=女, age=18, salary=8200, country=连云港)
Person(name=小王, sex=女, age=22, salary=10200, country=徐州)
Person(name=小刘, sex=男, age=24, salary=8200, country=苏州)
Person(name=小梁, sex=女, age=26, salary=5200, country=上海)
Person(name=小六, sex=男, age=19, salary=9200, country=无锡)
筛选大于20岁的第一个{"age":22,"country":"徐州","name":"小王","salary":10200,"sex":"女"}
筛选大于20岁的任意一个{"age":24,"country":"苏州","name":"小刘","salary":8200,"sex":"男"}
是否存在大于20岁的true
2、筛选 filter
筛选出来工资大于8000的小伙伴,并生成新的集合
// 筛选出来工资大于8000的小伙伴,并生成新的集合
List collect = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toList());
// 筛选出来工资大于8000的小伙伴,并生成新名称的集合
List collect1 = personList.stream().filter(p -> p.getSalary() > 8000).map(Person::getName).collect(Collectors.toList());
System.out.println("筛选出来工资大于8000的小伙伴"+ JSONObject.toJSONString(collect));
System.out.println("筛选出来工资大于8000的小伙伴的名称"+ JSONObject.toJSONString(collect1));
结果:
筛选出来工资大于8000的小伙伴[{"age":18,"country":"连云港","name":"小李","salary":8200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":10200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":8200,"sex":"男"},{"age":19,"country":"无锡","name":"小六","salary":9200,"sex":"男"}]
筛选出来工资大于8000的小伙伴的名称["小李","小王","小刘","小六"]
3、max、min、count
1)获取居住地名称最长的
// 获取家乡名称最长的
Optional max = personList.stream().map(Person::getCountry).max(Comparator.comparing(String::length));
System.out.println("人员居住地字符串长度最长的:"+max);
输出:
人员居住地字符串长度最长的:Optional[连云港]
2)筛选出来年龄最小的
// 筛选出来年龄最小
Optional min = personList.stream().map(Person::getAge).min(Integer::compareTo);
System.out.println("年龄最小的人员:"+min);
// 也可以自定义比较规则,Comparator
Optional min1 = personList.stream().map(Person::getAge).min(new Comparator() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
});
System.out.println("年龄最小的人员:"+min1);
输出:
年龄最小的人员:Optional[18] 年龄最小的人员:Optional[18]
3)获取工资大于8k的人员个数
// 获取工资大于8k的个数
long count = personList.stream().map(Person::getSalary).filter(p -> p > 8000).count();
System.out.println("获取工资大于8k的个数"+count);
输出:
获取工资大于8k的个数44、map和flatMap映射
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map和flatMap:
- map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
// 所有员工工资加上1k 1、不改变原来的集合,2、改变原来集合
// 1、不改变原来的集合,
List collect2 = personList.stream().map(person -> {
Person person1 = new Person(person.getName(), person.getSex(), person.getAge(), person.getSalary() + 1000, person.getCountry());
return person1;
}).collect(Collectors.toList());
System.out.println("1、不改变原来的集合,"+JSONObject.toJSONString(collect2));
// 改变原来集合
List collect3 = personList.stream().map(person -> {
person.setSalary(person.getSalary() + 1000);
return person;
}).collect(Collectors.toList());
System.out.println("2、改变原来集合"+collect3);
输出:
1、不改变原来的集合,[{"age":18,"country":"南京","name":"小张","salary":4200,"sex":"男"},{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"},{"age":26,"country":"上海","name":"小梁","salary":6200,"sex":"女"},{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}]
2、改变原来集合[Person(name=小张, sex=男, age=18, salary=4200, country=南京), Person(name=小李, sex=女, age=18, salary=9200, country=连云港), Person(name=小王, sex=女, age=22, salary=11200, country=徐州), Person(name=小刘, sex=男, age=24, salary=9200, country=苏州), Person(name=小梁, sex=女, age=26, salary=6200, country=上海), Person(name=小六, sex=男, age=19, salary=10200, country=无锡)]
Disconnected from the target VM, address: '127.0.0.1:8836', transport: 'socket'
5、reduce(归约)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
// reduce 归约
// 求和
Optional reduce = personList.stream().map(Person::getSalary).reduce(Integer::sum);
System.out.println("求和:"+reduce.get());
// 求和方式2
int sum = personList.stream().mapToInt(Person::getSalary).sum();
System.out.println("求和2:"+sum);
// 求和方式3
Optional reduce3 = personList.stream().map(Person::getSalary).reduce((x, y) -> x + y);
System.out.println("求和3:"+reduce3.get());
// 求和方式4
Integer reduce1 = personList.stream().map(Person::getSalary).reduce(1, Integer::sum);
System.out.println("求和4:"+reduce3.get());
// 求最大值
Optional reduce2 = personList.stream().map(Person::getSalary).reduce((x, y) -> x > y ? x : y);
System.out.println("求最大值:"+reduce2.get());
// 求最大值
Integer reduce4 = personList.stream().map(Person::getSalary).reduce(1, Integer::max);
System.out.println("求最大值:"+reduce4);
// 求乘积
Optional reduce5 = personList.stream().map(Person::getSalary).reduce((x, y) -> x * y);
System.out.println("求乘积:"+reduce5.get());
int i = 1;
for (Person person : personList){
i = i * person.getSalary();
}
System.out.println("求乘积:"+i);
输出结果:
求和:50200 求和2:50200 求和3:50200 求和4:50200 求最大值:11200 求最大值:11200 求乘积:-1149239296 求乘积:-11492392966、collect(收集)
流最终的操作还是要把处理过的数据给收集起来,可能是原来的集合,也可能是一个新的集合
6.1、toList,toMap,toSet// collect收集
// 常用的toList,toMap,toSet等
// 工资大于8k的集合
List collect4 = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toList());
System.out.println("工资大于8k的集合:"+JSONObject.toJSONString(collect4));
// 工资大于8k的人员姓名
List collect5 = personList.stream().filter(p -> p.getSalary() > 8000).map(Person::getName).collect(Collectors.toList());
System.out.println("工资大于8k的人员姓名:"+JSONObject.toJSONString(collect5));
// 工资大于8k的人员姓名 set
Set collect6 = personList.stream().filter(p -> p.getSalary() > 8000).map(Person::getName).collect(Collectors.toSet());
System.out.println("工资大于8k的人员姓名:"+JSONObject.toJSONString(collect6));
// 工资大于8k的人员姓名 map
Map collect7 = personList.stream().filter(p -> p.getSalary() > 8000).collect(Collectors.toMap(Person::getName, Person::getSalary));
System.out.println("工资大于8k的人员姓名map:"+JSONObject.toJSONString(collect7));
结果:
工资大于8k的集合:[{"age":18,"country":"连云港","name":"小李","salary":9200,"sex":"女"},{"age":22,"country":"徐州","name":"小王","salary":11200,"sex":"女"},{"age":24,"country":"苏州","name":"小刘","salary":9200,"sex":"男"},{"age":19,"country":"无锡","name":"小六","salary":10200,"sex":"男"}]
工资大于8k的人员姓名:["小李","小王","小刘","小六"]
工资大于8k的人员姓名:["小刘","小李","小六","小王"]
工资大于8k的人员姓名map:{"小刘":9200,"小李":9200,"小六":10200,"小王":11200}
6



