Java8 Stream相关
文章转载于https://mp.weixin.qq.com/s/K40HSvLVGpLUrdEz0ZTzng
java8的stream相关的用法以及技术点,如下图所示
Stream概述
Stream
将要处理的元素集合看作一种流,在流的过程中,借助Stream API
对流中的元素进行操作,比如:筛选、排序、聚合等。
Stream操作类型
Stream
的操作主要分为以下两种类型:
- 中间操作,每次返回一个新的流,可以有多个。
- 终端操作,每个流只能进行一次终端操作,终端操作结束后流无法再次使用。终端操作会产生一个新的集合或值。
Stream特性
Stream
有几个特性:
- stream不存储数据,而是按照特定的规则对数据进行计算,一般会输出结果。
- stream不会改变数据源,通常情况下会产生一个新的集合或一个值。
- stream具有延迟执行特性,只有调用终端操作时,中间操作才会执行。
Stream的创建
Stream
可以通过集合数组创建。
1.java.util.Collection.stream()方法创建
/**
* 1.java.util.Collection.stream()方法创建
*/
public static void typeOne(){
List<Integer> list = Arrays.asList(1,2,3,4,5,6);
//创建一个顺序流
Stream<Integer> stream = list.stream();
//创建一个并行流
Stream<Integer> integerStream = list.parallelStream();
}
2.java.util.Arrays.stream(T[] array)方法用数组创建流
/**
* 2.java.util.Arrays.stream(T[] array)方法用数组创建流
*/
public static void typeTwo(){
int [] array = {1,2,3,4,5,6};
IntStream stream = Arrays.stream(array);
}
3.使用Stream的静态方法:of()、iterate()、generate()
/**
*3.使用Stream的静态方法:of()、iterate()、generate()
*/
public static void typeThree(){
Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
Stream<Integer> stream2 = Stream.iterate(0, (x) -> x + 3).limit(4);
Stream<Double> stream3 = Stream.generate(Math::random).limit(3);
}
顺序流和并行流的区别
stream
是顺序流,由主线程按顺序对流执行操作
parallelStream
是并行流,内部以多线程并行执行的方式对流进行操作
在处理筛选集合中的奇数,两者的处理流程不同,操作过程示例如下:
如果流中数据量很大,并行流可以加快处理速度。
除了直接创建并行流,还可以通过parallel()
把顺序流转换成并行流
Stream<Integer> parallel = list.stream().parallel();
Stream流的使用
1.遍历/匹配
Stream
也是支持类似集合的遍历和匹配元素的,只是Stream
中的元素是以Optional
类型存在的。
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 5, 7, 6, 9, 4, 3, 1);
//遍历输出元素
System.out.println("遍历输出:");
list.stream().forEach(System.out::println);
//匹配第一个
Optional<Integer> first = list.stream().findFirst();
System.out.println("匹配第一个值:"+first.get());
//匹配任意一个值(使用与并行流)
Optional<Integer> any = list.stream().parallel().findAny();
System.out.println("匹配任意一个值:"+any.get());
boolean b = list.stream().anyMatch(x -> x > 5);
System.out.println("是否存在大于5的值:"+b);
}
2.筛选
筛选,是按照一定的规则校验流中的元素,将符合条件的元素提取到新的流中的操作。
筛选集合中的元素并打印
//1.打印集合中大于7的元素
List<Integer> list = Arrays.asList(1, 5, 7, 6, 9, 4, 3, 1);
list.stream().filter(s->s>7).forEach(System.out::println);
筛选集合中的元素组成新的集合
/**
* 初始化list数组
*/
public static void initEmployee() {
employees.add(Employee.builder().name("Tom").age(25).salary(6000).address("New York").sex(0).build());
employees.add(Employee.builder().name("Jack").age(28).salary(10000).address("New York").sex(0).build());
employees.add(Employee.builder().name("Lily").age(30).salary(7000).address("Washington").sex(1).build());
employees.add(Employee.builder().name("Anni").age(32).salary(15000).address("New York").sex(1).build());
employees.add(Employee.builder().name("Owen").age(22).salary(6500).address("Washington").sex(0).build());
employees.add(Employee.builder().name("Alisa").age(21).salary(8000).address("New York").sex(1).build());
}
//2.过滤集合中工资高于10000的员工
List<Employee> collect = employees.stream().filter(s -> s.getSalary() >= 10000).collect(Collectors.toList());
//打印满足条件的员工的姓名
collect.forEach(s-> System.out.println(s.getName()));
3.聚合(max/min/count)
max
、min
、count
通常用于数据统计
获取string集合中最长的元素
//1.获取字符串中字符最长的字符串
List<String> list = Arrays.asList("Tom","Jack","Lily","Anni","Owen","jamsibul");
Optional<String> max = list.stream().max(Comparator.comparing(String::length));
System.out.println("最长的字符串:"+max.get());
获取Integer集合中的最大值
//2.获取集合中最小的数字
List<Integer> listInt = Arrays.asList(1,8,6,7,4,5,9,1,0,9);
Optional<Integer> min = listInt.stream().min(Integer::compareTo);
System.out.println("最小的数字:"+min.get());
获取员工工资最高的人
//3.获取员工工资最高的人
Optional<Employee> maxEmployee = employees.stream().max(Comparator.comparing(Employee::getSalary));
System.out.println("员工工资最高的人:"+maxEmployee.get().getName());
获取Integer集合中大于6的元素个数
//4.获取集合中大于5的元素个数
long count = listInt.stream().filter(s -> s > 5).count();
System.out.println("获取集合中大于5的元素个数:"+count);
4.映射
映射,可以将一个流的元素按照一定的映射规则映射到另一个流中。分为map
和flatMap
:
map
:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。flatMap
:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
英文字符串数组的元素全部改为大写。整数数组每个元素+3
//1.英文字符串数组的元素全部改为大写
String [] str = {"abc","afc","erewfs","gd"};
List<String> collect = Arrays.stream(str).map(String::toUpperCase).collect(Collectors.toList());
System.out.println("每个元素大写:"+collect);
//2.整数数组每个元素+3
List<Integer> intList = Arrays.asList(1,5,6,7,4,9,5);
List<Integer> collect1 = intList.stream().map(s -> s + 3).collect(Collectors.toList());
System.out.println("每个元素加3:"+collect1);
System.out.println("调整前工资:"+employees.get(0).getName()+":"+employees.get(0).getSalary());
将员工的薪资全部增加1000
//3.将员工的薪资全部增加1000
List<Employee> collect2 = employees.stream().map(s -> {
s.setSalary(s.getSalary() + 1000);
return s;
}).collect(Collectors.toList());
System.out.println("每个员工工资加1000:"+collect2.get(0).getName()+":"+collect2.get(0).getSalary());
将两个字符数组合并成一个新的字符数组
//4.将两个字符数组合并成一个新的字符数组
List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
List<String> listNew = list.stream().flatMap(s -> {
// 将每个元素转换成一个stream
String[] split = s.split(",");
Stream<String> s2 = Arrays.stream(split);
return s2;
}).collect(Collectors.toList());
System.out.println("处理前的集合:" + list);
System.out.println("处理后的集合:" + listNew);
5.归约(reduce)
归约,也称缩减,顾名思义,是把一个流缩减成一个值,能实现对集合求和、求乘积和求最值操作。
求Integer集合的元素之和、乘积和最大值
public static void testReduce(){
List<Integer> list = Arrays.asList(1, 3, 4, 6, 7, 9, 4, 10);
//1.求和
Optional<Integer> sum = list.stream().reduce(Integer::sum);
System.out.println("求和:"+sum.get());
//2.求乘积
Optional<Integer> product = list.stream().reduce((x, y) -> x * y);
System.out.println("求乘积:"+product.get());
//3.求最大值
Optional<Integer> max = list.stream().reduce(Integer::max);
System.out.println("求最大值:"+max.get());
}
求所有员工的工资之和和最高工资
//4.求所有员工的工资之和和最高工资
Optional<Integer> countSalary = employees.stream().map(Employee::getSalary).reduce(Integer::sum);
System.out.println("所有员工的工资之和:"+countSalary.get());
Integer reduce = employees.stream().reduce(0, (max, e) -> max > e.getSalary() ? max : e.getSalary(), Integer::max);
System.out.println("员工最高工资:"+reduce);
6.收集
collect
,收集,把一个流收集起来,最终收集成一个值或者一个新的集合。
collect
主要依赖java.util.stream.Collectors
类内置的静态方法。
6.1归集
因为流不存储数据,那么在流中的数据完成处理后,需要将流中的数据重新归集到新的集合里。toList
、toSet
和toMap
比较常用,另外还有toCollection
、toConcurrentMap
等复杂一些的用法。
public class SimpleDemo05 {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 5, 6, 7, 8, 9, 4);
List<Integer> toList = list.stream().filter(s -> s > 5).collect(Collectors.toList());
Set<Integer> toSet = list.stream().filter(s -> s > 5).collect(Collectors.toSet());
List<Student> studentList = Arrays.asList(Student.builder().age(10).name("00").build(), Student.builder().age(11).name("11").build());
Map<String, Student> toMap = studentList.stream().collect(Collectors.toMap(Student::getName, s -> s));
System.out.println(toList);
System.out.println(toSet);
System.out.println(toMap);
}
}
6.2统计
Collectors
提供了一系列用于数据统计的静态方法:
- 计数:
count
- 平均值:
averagingInt
、averagingLong
、averagingDouble
- 最值:
maxBy
、minBy
- 求和:
summingInt
、summingLong
、summingDouble
- 统计以上所有:
summarizingInt
、summarizingLong
、summarizingDouble
/**
* 统计
* @author likai
* @Date 2022/3/31 14:38
*/
public class SimpleDemo06 {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(Student.builder().age(10).name("00").build(), Student.builder().age(11).name("11").build());
// 求总数
Long count = studentList.stream().collect(Collectors.counting());
// 求平均
Double average = studentList.stream().collect(Collectors.averagingDouble(Student::getAge));
// 求最高
Optional<Integer> max = studentList.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare));
// 求之和
Integer sum = studentList.stream().collect(Collectors.summingInt(Student::getAge));
// 一次性统计所有信息
DoubleSummaryStatistics collect = studentList.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("总数:" + count);
System.out.println("平均:" + average);
System.out.println("总和:" + sum);
System.out.println("所有统计:" + collect);
}
}
6.3分组
将stream
按条件分为多个Map
/**
* 分组
* @author likai
* @Date 2022/3/31 14:43
*/
public class SimpleDemo07 {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
Student.builder().age(10).name("00").sex(0).address("hubei").build(),
Student.builder().age(11).name("11").sex(0).address("hubei").build(),
Student.builder().age(22).name("22").sex(1).address("hunan").build(),
Student.builder().age(33).name("33").sex(1).address("hunan").build()
);
// 按年龄是否大于20分组
Map<Boolean, List<Student>> part = studentList.stream().collect(Collectors.partitioningBy(x -> x.getAge() > 20));
// 将性别分组
Map<Integer, List<Student>> group = studentList.stream().collect(Collectors.groupingBy(Student::getSex));
// 先按性别分组,再按地区分组
Map<Integer, Map<String, List<Student>>> group2 = studentList.stream().collect(Collectors.groupingBy(Student::getSex, Collectors.groupingBy(Student::getAddress)));
System.out.println("按年龄是否大于20分组:" + part);
System.out.println("将性别分组:" + group);
System.out.println("先按性别分组,再按地区分组:" + group2);
}
}
6.4接合
joining
可以将stream中的元素用特定的连接符(没有的话,则直接连接)连接成一个字符串。
/**
* 接合
* @author likai
* @Date 2022/3/31 14:51
*/
public class SimpleDemo08 {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
Student.builder().age(10).name("00").sex(0).address("hubei").build(),
Student.builder().age(11).name("11").sex(0).address("hubei").build(),
Student.builder().age(22).name("22").sex(1).address("hunan").build(),
Student.builder().age(33).name("33").sex(1).address("hunan").build()
);
String name = studentList.stream().map(s -> s.getName()).collect(Collectors.joining("-"));
System.out.println(name);
}
}
6.5规约
Collectors
类提供的reducing
方法,相比于stream
本身的reduce
方法,增加了对自定义归约的支持。
/**
* 规约
* @author likai
* @Date 2022/3/31 16:19
*/
public class SimpleDemo09 {
public static void main(String[] args) {
List<Student> studentList = Arrays.asList(
Student.builder().age(10).name("00").sex(0).address("hubei").build(),
Student.builder().age(11).name("11").sex(0).address("hubei").build(),
Student.builder().age(22).name("22").sex(1).address("hunan").build(),
Student.builder().age(33).name("33").sex(1).address("hunan").build()
);
// Collectors的reduce
Integer sum = studentList.stream().collect(Collectors.reducing(10, Student::getAge, (i, j) -> (i + j)));
System.out.println("年龄总和:" + sum);
// stream的reduce
Optional<Integer> sum2 = studentList.stream().map(Student::getAge).reduce(Integer::sum);
System.out.println("年龄总和:" + sum2.get());
}
}
6.6排序
sorted,中间操作。有两种排序:
- sorted():自然排序,流中元素需实现Comparable接口
- sorted(Comparator com):Comparator排序器自定义排序
/**
* 排序
* @author likai
* @Date 2022/3/31 16:24
*/
public class SimpleDemo10 {
public static void main(String[] args) {
List<Student> studentList = DemoStudentList.getStudentList();
// 按年龄升序排序(自然排序)
List<String> newList = studentList.stream().sorted(Comparator.comparing(Student::getAge)).map(Student::getName)
.collect(Collectors.toList());
// 按年龄倒序排序
List<String> newList2 = studentList.stream().sorted(Comparator.comparing(Student::getAge).reversed())
.map(Student::getName).collect(Collectors.toList());
// 先按年龄再按身高升序排序
List<String> newList3 = studentList.stream()
.sorted(Comparator.comparing(Student::getAge).thenComparing(Student::getHeight)).map(Student::getName)
.collect(Collectors.toList());
// 先按年龄再按身高升序排序
List<String> newList4 = studentList.stream().sorted((p1, p2) -> {
if (p1.getAge() == p2.getAge()) {
return p2.getAge() - p1.getAge();
} else {
return p2.getHeight() - p1.getHeight();
}
}).map(Student::getName).collect(Collectors.toList());
System.out.println("按年龄升序排序:" + newList);
System.out.println("按年龄倒序排序:" + newList2);
System.out.println("先按年龄再按身高升序排序:" + newList3);
System.out.println("先按年龄再按身高升序排序:" + newList4);
}
}
6.7提取/组合
流也可以进行合并、去重、限制、跳过等操作。
/**
* 提取/组合
* @author likai
* @Date 2022/3/31 17:16
*/
public class SimpleDemo11 {
public static void main(String[] args) {
String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "d", "e", "f", "g" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
// concat:合并两个流 distinct:去重
List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
// limit:限制从流中获得前n个数据
List<Integer> collect = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
// skip:跳过前n个数据
List<Integer> collect2 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());
System.out.println("流合并:" + newList);
System.out.println("limit:" + collect);
System.out.println("skip:" + collect2);
}
}