「这是我参与2022首次更文挑战的第25天,活动详情查看:2022首次更文挑战」
相信目前大部分的公司Java版本还是停留在8的版本,但是有的人对于Java8的一个新特性还不是很了解,就是Stream,配合起同版本出现的Lambda,在平时写代码的过程中还是提供了很高的便利性的。下面就说说关于Stream的常用的用法,一起来玩转Stream吧。
Stream将要处理的元素集合看作一种流,在流的过程中,借助Stream API对流中的元素进行操作,比如:筛选、排序、聚合等。
-
使用Stream初始化 如初始化一个list:
List<String> list1 = Stream.of("a", "b", "c").collect(Collectors.toList()); -
创建一个流
Stream<String> stream1 = list1.stream();一般我们这个创建的是一个顺序流,由主线程按顺序对流进行逻辑操作,在一些不需要注意顺序的情况下,如:过滤,我们可以创建一个并行流,以多线程并行执行的方式对流进行操作。
创建并行流
Stream<String> parallelStream1 = list1.parallelStream();也可以通过
parallel()把顺序流转换成并行流Stream<String> parallelStream2 = list1.stream().parallel();
Stream之filter
filter,筛选,根据定义的规则校验流中的元素,将满足规则的元素提取到新的流中
先构造一个Student类
@Data
public class Student {
private String name;
private int age;
private Integer grade;
private Double score;
public Student(String name, int age, Integer grade, Double score) {
this.name = name;
this.age = age;
this.grade = grade;
this.score = score;
}
}
filter list列表中,grade字段不为null 以及 大于6的元素
public class BaseTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, null, null)
).collect(Collectors.toList());
//顺序流filter
list.stream().filter(student -> student.getGrade() != null && student.getGrade() > 6).forEach(System.out::println);
//形成新的集合
List<Student> studentList = list.stream().filter(student -> student.getGrade() != null && student.getGrade() > 6).collect(Collectors.toList());
System.out.println("*********************");
//并行流流filter
list.stream().parallel().filter(student -> student.getGrade() != null && student.getGrade() > 6).forEach(System.out::println);
}
}
输出结果:
Student(name=李阳, age=20, grade=12, score=512.0)
Student(name=张三, age=18, grade=10, score=529.0)
Student(name=李四, age=17, grade=10, score=300.0)
*********************
Student(name=张三, age=18, grade=10, score=529.0)
Student(name=李四, age=17, grade=10, score=300.0)
Student(name=李阳, age=20, grade=12, score=512.0)
从这里的结果可以明显看出,顺序流是按照列表的顺序一个个元素操作的,并行流无法保证顺序。
Stream之find/match
Stream是支持类似集合的遍历和匹配元素的,Stream中的元素是以Optional类型存在的。
Optional是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
public class BaseTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, null, null)
).collect(Collectors.toList());
// 匹配第一个
Optional<Student> findFirst = list.stream().filter(student -> student.getGrade() != null && student.getGrade() > 6).findFirst();
// 匹配任意(适用于并行流)
Optional<Student> findAny = list.parallelStream().filter(student -> student.getGrade() != null && student.getGrade() > 6).findAny();
// 是否包含符合特定条件的元素
boolean anyMatch = list.stream().anyMatch(student -> student.getGrade() > 6);
System.out.println("匹配第一个findFirst:" + findFirst.get());
System.out.println("匹配任意findAny:" + findAny.get());
System.out.println("是否包含符合特定条件的元素anyMatch:" + anyMatch);
}
}
输出结果:
匹配第一个findFirst:Student(name=李阳, age=20, grade=12, score=512.0)
匹配任意findAny:Student(name=张三, age=18, grade=10, score=529.0)
是否包含符合特定条件的元素anyMatch:true
Stream之max/min/count
在mysql中,经常会使用max、min、count来帮助我们进行数据统计,在Java8 Stream中也引入了这些概念和用法,方便了我们对集合中的数据统计。
public class BaseTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, null, null)
).collect(Collectors.toList());
//自然排序
Optional<Student> max = list.stream().max(Comparator.comparing(Student::getAge));
Optional<Student> min = list.stream().min(Comparator.comparing(Student::getAge));
System.out.println("年龄最大的学生:" + max.get());
System.out.println("年龄最小的学生:" + min.get());
//自定义排序
Optional<Student> custom = list.stream().max(new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o1.hashCode() >= o2.hashCode() ? -1 : 1;
}
});
System.out.println("hashCode最大的Student的名字:" + custom.get().getName());
long count = list.stream().filter(student -> student.getAge() > 15).count();
System.out.println("年龄大于15的总数:" + custom.get().getName());
}
}
输出结果:
年龄最大的学生:Student(name=隔壁老王, age=50, grade=null, score=null)
年龄最小的学生:Student(name=小明, age=10, grade=3, score=100.0)
hashCode最大的Student的名字:李四
年龄大于15的总数:4
可以看出在使用max min时,我们可以通过自定义Comparator排序来实现
Stream之map/flatMap
map:接收一个函数作为参数,将该函数应用到流的每一个元素中,并将其映射到一个新的元素上去flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
public class BaseTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, null, null)
).collect(Collectors.toList());
//不改变原来集合list
List<Integer> collect1 = list.stream().map(student -> student.getAge() + 10).collect(Collectors.toList());
for (int i = 0; i < list.size(); i++) {
System.out.println("原age:" + list.get(i).getAge() + ";新age:" + collect1.get(i));
}
//合并成一个新的list
List<String> strList = Stream.of("h,e,l,l,o", "w,o,r,d").collect(Collectors.toList());
List<String> collect2 = strList.stream().flatMap(str -> Stream.of(str.split(","))).collect(Collectors.toList());
System.out.println(collect2);
//与flatMap对比,结果是两个String[]的数组
List<String[]> collect3 = strList.stream().map(str -> str.split(",")).collect(Collectors.toList());
System.out.println(collect3);
}
}
输出结果:
原age:10;新age:20
原age:11;新age:21
原age:20;新age:30
原age:18;新age:28
原age:17;新age:27
原age:50;新age:60
[h, e, l, l, o, w, o, r, d]
[[Ljava.lang.String;@45ff54e6, [Ljava.lang.String;@2328c243]
通过最后的 collect2 和 collect3对比可以看到,map和flatMap的区别了。map原来的流里有几个元素,操作后还是有几个元素的存在的。
Stream之reduce
reduce,归约,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
public class BaseTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, null, null)
).collect(Collectors.toList());
//求和的三种写法
Optional<Integer> reduce1 = list.stream().map(Student::getAge).reduce(Integer::sum);
Integer reduce2 = list.stream().reduce(0, (sum, o) -> sum = sum + o.getAge(), (sum1, sum2) -> sum1 + sum2);
Integer reduce3 = list.stream().reduce(0, (sum, o) -> sum = sum + o.getAge(), Integer::sum);
System.out.println("reduce1:" + reduce1.get());
System.out.println("reduce2:" + reduce2);
System.out.println("reduce3:" + reduce3);
//求最大值的二种写法
Integer max1 = list.stream().map(Student::getAge).reduce(1, Integer::max);
Integer max2 = list.stream().reduce(0, (maxAge, o) -> maxAge > o.getAge() ?
maxAge : o.getAge(), Integer::max);
System.out.println("max1:" + max1);
System.out.println("max2:" + max2);
}
}
输出结果:
reduce1:126
reduce2:126
reduce3:126
max1:50
max2:50
写法不局限于一种,个人最喜欢第一个写法,因为够简洁,其余的写法感觉没有发挥出Stream便利的特性;当然,见仁见智。
Stream之collect
collect,收集,把一个流收集起来,可以收集成一个值或者一个新的集合
collect 之toList/toSet/toMap
流不存储数据,所以我们一般在流处理完数据后,都将流的数据重新归集到新的集合里,这不局限于toList/toSet/toMap,只是工作中比较常见,还有如toConcurrentMap。
public class CollectTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, 9, 100.0)
).collect(Collectors.toList());
Set<Student> set = list.stream().filter(o -> o.getAge() % 2 == 0).collect(Collectors.toSet());
Map<?, Student> map = list.stream().filter(o -> o.getAge() >= 18)
.collect(Collectors.toMap(Student::getName, o -> o));
System.out.println(list);
System.out.println(set);
System.out.println(map);
}
}
输出结果:
[Student(name=小明, age=10, grade=3, score=100.0), Student(name=小红, age=11, grade=3, score=88.0), Student(name=李阳, age=20, grade=12, score=512.0), Student(name=张三, age=18, grade=10, score=529.0), Student(name=李四, age=17, grade=10, score=300.0), Student(name=隔壁老王, age=50, grade=9, score=100.0)]
[Student(name=小明, age=10, grade=3, score=100.0), Student(name=李阳, age=20, grade=12, score=512.0), Student(name=隔壁老王, age=50, grade=9, score=100.0), Student(name=张三, age=18, grade=10, score=529.0)]
{张三=Student(name=张三, age=18, grade=10, score=529.0), 隔壁老王=Student(name=隔壁老王, age=50, grade=9, score=100.0), 李阳=Student(name=李阳, age=20, grade=12, score=512.0)}
collect 之统计
Collectors 提供了很多数据统计的静态方法,有一些其实Stream也提供了,但是平时还是使用Collectors提供的多。
- 计数:
count - 平均值:
averagingInt、averagingLong、averagingDouble - 最值:
maxBy、minBy - 求和:
summingInt、summingLong、summingDouble - 统计以上所有:
summarizingInt、summarizingLong、summarizingDouble
public class CollectTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, 9, 100.0)
).collect(Collectors.toList());
// 求总数
Long count = list.stream().collect(Collectors.counting());
// 求平均工资
Double average = list.stream().collect(Collectors.averagingInt(Student::getAge));
// 求最高工资
Optional<Integer> max = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare));
// 求工资之和
Integer sum = list.stream().collect(Collectors.summingInt(Student::getAge));
// 一次性统计所有信息
DoubleSummaryStatistics collect = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("人数:" + count);
System.out.println("平均年龄:" + average);
System.out.println("年龄总和:" + sum);
System.out.println("年龄所有统计:" + collect);
}
}
输出结果:
人数:6
平均年龄:21.0
年龄总和:126
年龄所有统计:DoubleSummaryStatistics{count=6, sum=126.000000, min=10.000000, average=21.000000, max=50.000000}
collect 之partitioningBy/groupingBy
partitioningBy,分区,将stream按条件分为两个MapgroupingBy,分组,将集合按条件分组
public class CollectTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, 9, 100.0)
).collect(Collectors.toList());
//按照年龄是否大于18 分成两组
Map<Boolean, List<Student>> part = list.stream().collect(Collectors.partitioningBy(x -> x.getAge() >= 18));
//按照年级grade分组
Map<Integer, List<Student>> collect = list.stream().collect(Collectors.groupingBy(Student::getGrade));
//按照年级grade、得分score分组
Map<Double, Map<Integer, List<Student>>> group = list.stream().collect(Collectors.groupingBy(Student::getScore, Collectors.groupingBy(Student::getGrade)));
System.out.println(part);
System.out.println(collect);
System.out.println(group);
}
}
输出结果:
{false=[Student(name=小明, age=10, grade=3, score=100.0), Student(name=小红, age=11, grade=3, score=88.0), Student(name=李四, age=17, grade=10, score=300.0)], true=[Student(name=李阳, age=20, grade=12, score=512.0), Student(name=张三, age=18, grade=10, score=529.0), Student(name=隔壁老王, age=50, grade=9, score=100.0)]}
{3=[Student(name=小明, age=10, grade=3, score=100.0), Student(name=小红, age=11, grade=3, score=88.0)], 9=[Student(name=隔壁老王, age=50, grade=9, score=100.0)], 10=[Student(name=张三, age=18, grade=10, score=529.0), Student(name=李四, age=17, grade=10, score=300.0)], 12=[Student(name=李阳, age=20, grade=12, score=512.0)]}
{529.0={10=[Student(name=张三, age=18, grade=10, score=529.0)]}, 512.0={12=[Student(name=李阳, age=20, grade=12, score=512.0)]}, 300.0={10=[Student(name=李四, age=17, grade=10, score=300.0)]}, 88.0={3=[Student(name=小红, age=11, grade=3, score=88.0)]}, 100.0={3=[Student(name=小明, age=10, grade=3, score=100.0)], 9=[Student(name=隔壁老王, age=50, grade=9, score=100.0)]}}
collect 之joining
joining,顾名思义连接,可以将流中的元素按照特定的字符连接起来成一个字符串
public class CollectTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, 9, 100.0)
).collect(Collectors.toList());
String names = list.stream().map(s -> s.getName()).collect(Collectors.joining(","));
System.out.println(names);
}
}
输出结果:
小明,小红,李阳,张三,李四,隔壁老王
collect 之reducing
Collectors类提供的reducing方法,对比于stream本身的reduce方法,增加了对自定义归约的支持。
public class CollectTest {
public static void main(String[] args) {
List<Student> list = Stream.of(
new Student("小明", 10, 3, 100.0),
new Student("小红", 11, 3, 88.0),
new Student("李阳", 20, 12, 512.0),
new Student("张三", 18, 10, 529.0),
new Student("李四", 17, 10, 300.0),
new Student("隔壁老王", 50, 9, 100.0)
).collect(Collectors.toList());
Double sum = list.stream().collect(Collectors.reducing(0.0, Student::getScore, (i, j) -> (i + j * 0.6)));
System.out.println("折算后的总分:" + sum);
}
}
输出结果:
折算后的总分:977.4
这个平时我也没怎么用到,要是有更好的例子,欢迎评论留言
collect 之distinct/skip/limit
public class CollectTest {
public static void main(String[] args) {
List<String> list1 = Arrays.asList("a", "b", "c");
List<String> list2 = Stream.of("a", "b", "d", "e").collect(Collectors.toList());
Stream<String> stream1 = list1.stream();
Stream<String> stream2 = list2.stream();
//合并,相当于去重
List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
//如果没有limit,这个流就不会退出,相当于无限for循环
//limit(10),类似于mysql的limit
List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
//skip(n) 跳过n个元素
List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(3).limit(10).collect(Collectors.toList());
}
}
输出结果:
[a, b, c, d, e]
[1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
[7, 9, 11, 13, 15, 17, 19, 21, 23, 25]
好了,以上就是本篇的全部内容了,感觉坚持看到这里,相信对你也有一定的收获,有收获的话可以动动小手点个赞,