说明:
java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个则是Stream API。
Stream API(java.util.stream)把真正的函数式编程风格引入java。这是目前为止对java类库最好的补充,因为Stream API可以极大提供java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream是java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用sql执行的数据库查询。也可以使用Stream API并执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式
为什么使用Stream API?
实际开发中,项目中多数数据源来自于Mysql、Oracle等。但现在数据源可以更多了,有mondb,redis等,而这些NoSql的数据就需要jaav层面去处理。
什么是Stream?
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
Stream和Collection集合区别?
1.Collection是一种静态的内存数据结构,讲的是数据,而Stream是有关计算的,讲的是计算。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。
2.Stream API关注的是多个数据的计算(排序、查找、过滤、映射、遍历等)
3.集合关注的是数据的存储,面向内存的
使用说明?
1.Stream自己不会存储元素。(针对数据计算)
2.Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
3.Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
4.Stream一旦执行了终止操作,就不能再调用其它中间操作或终终止操作了~
操作步骤
Stream的实例化
一系列的中间操作
执行终止操作
数据准备:
创建Employee类
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 package data; import com.oracle.webservices.internal.api.databinding.DatabindingMode; public class Employee { private long id; private String name; private int age; private double salary; public Employee(long id,String name,int age,double salary){ this.id = id; this.age = age; this.name = name; this.salary = salary; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString(){ return "Employee{"+"id="+id+",name="+name+",age="+age+",salary="+salary+"}"; } }
创建EmployeeData类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package data; import java.util.ArrayList; import java.util.List; public class EmployeeData { public static List<Employee> getEmployees(){ List<Employee> list = new ArrayList<>(); list.add(new Employee(1001,"马化腾",34,6000.38)); list.add(new Employee(1002,"马云",2,9876.38)); list.add(new Employee(1003,"刘强东",33,3000.82)); list.add(new Employee(1004,"雷军",26,7657.37)); list.add(new Employee(1005,"李彦宏",65,5555.38)); list.add(new Employee(1006,"比尔盖茨",42,9500.38)); list.add(new Employee(1007,"任正非",34,4333.32)); list.add(new Employee(1008,"扎克伯格",35,2500.32)); return list; } }
1.创建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 package streamTest; import data.Employee; import data.EmployeeData; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamAPITest { //创建Stream方式一:通过集合 @Test public void test1(){ List<Employee> list = EmployeeData.getEmployees(); //返回一个shuixu Stream<Employee> stream = list.stream(); //返回一个并行流 Stream<Employee> stream1 = list.parallelStream(); } //创建Stream方式二:通过数组 @Test public void test2(){ //调用Arrays类的static<T> Stream<T> stream(T[] array):返回一个流 Integer[] arr = new Integer[]{1,2,3,4,5}; Stream<Integer> stream = Arrays.stream(arr); int[] arr1 = new int[]{1,2,3,4,5,6,7}; IntStream stream1 = Arrays.stream(arr1); } //创建Stream方式大三:通过Stream的of() @Test public void text3(){ Stream<String> stream = Stream.of("AA", "BB", "C"); } }
2.一系列的在中间操作 1.筛选与切片
2.映射
3.排序
1.筛选与切片 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 package streamTest; import data.Employee; import data.EmployeeData; import org.junit.jupiter.api.Test; import java.util.List; import java.util.stream.Stream; public class StreamAPITest1 { //1-筛选与切片 @Test public void test(){ //filter(Predicate p)——接收Lambda,从流中排除某些元素 //查询员工表中薪资大于7000的员工信息 List<Employee> list = EmployeeData.getEmployees(); System.out.println(list); Stream<Employee> stream = list.stream(); stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println); System.out.println(); //limit(n)——截断流,使其元素不超过给定数量 //错误的。因为stream已经执行了终止操作,就不可以在调用其他的中间操作或终止操作了 // stream.limit(2).forEach(System.out::println); list.stream().filter(emp -> emp.getSalary()>7000).limit(2).forEach(System.out::println); // skip(n) ——跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,返回一个空流。 list.stream().skip(5).forEach(System.out::println); } }
注意:如果stream已经执行了终止操作,不可以在调用其他的中间操作或终止操作
2.映射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //2-映射 @Test public void test2() { //map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被映射到每个元素上 //转换为大写 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); //方式一: list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); //方式二: list.stream().map(String::toUpperCase).forEach(System.out::println); //获取员工姓名长度大于3的员工 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().filter(employee -> employee.getName().length()>3).forEach(System.out::println); //获取员工姓名长度大于3的员工的姓名 employees.stream().filter(employee -> employee.getName().length()>3).map(employee -> employee.getName()).forEach(System.out::println); }
3.排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //3-排序 @Test public void test3(){ //sorted()——自然排序 Integer[] arr = new Integer[]{ 342,3,64,46,7,3,54,65,68}; String[] arr1 = new String[]{"GG","DD","MM","SS","JJ"}; Arrays.stream(arr).sorted().forEach(System.out::println);//arr数组并没有因为升序做调整 Arrays.stream(arr1).sorted().forEach(System.out::println);//arr数组并没有因为升序做调整 //因为Employee没有实现Comparable接口,所以报错! // List<Employee> list = EmployeeData.getEmployees(); // list.stream().sorted().forEach(System.out::println); //sorted(Comparator com)——定制排序 List<Employee> list = EmployeeData.getEmployees(); //升序 list.stream().sorted((e1,e2)-> e1.getAge()- e2.getAge()).forEach(System.out::println); System.out.println(); //降序 list.stream().sorted((e1,e2)-> e2.getAge()- e1.getAge()).forEach(System.out::println); //针对字符串从大到小 Arrays.stream(arr1).sorted((s1,s2)->s2.compareTo(s1)).forEach(System.out::println); }
注意:Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
3.终止操作 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 package streamTest; import data.Employee; import data.EmployeeData; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; /** * 终止操作 */ public class StreamAPITest2 { //1-匹配与查找 @Test public void test(){ //是否所有的员工的年龄都大于18 //allMatch(Predicate p)——检查是否匹配所有元素 List<Employee> list = EmployeeData.getEmployees(); System.out.println(list.stream().allMatch(emp -> emp.getAge() > 18 )); //是否存在年龄大于18岁的员工 //anyMatch(Predicate p)——检查是否至少匹配一个元素 System.out.println(list.stream().anyMatch(emp -> emp.getAge() > 18)); //是否存在员工的工资大于10000 System.out.println(list.stream().anyMatch(emp -> emp.getSalary() > 10000)); //findFirst——返回第一个元素 System.out.println(list.stream().findFirst().get()); } @Test public void test2(){ List<Employee> list = EmployeeData.getEmployees(); System.out.println(list.stream().filter(employee -> employee.getSalary()>7000).count()); //max(Comparator c)——返回流中的最大值 //返回最高工资的员工 System.out.println(list.stream().max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))); //返回最低的工资 //方式一: System.out.println(list.stream().max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary())).get().getSalary()); //方式二: System.out.println(list.stream().map(employee -> employee.getSalary()).max((e1,e2)->Double.compare(e1, e2)).get()); //min(Comparator c)——返回流中的最小值 //返回最低工资的员工 System.out.println(list.stream().min((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))); //forEach(Consumer c)——内部迭代 list.stream().forEach(System.out::println); //针对于集合,jdk8中增加了一个遍历的方法 list.forEach(System.out::println); } //归约 @Test public void test3(){ //reduce(T identity,BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回T //计算1-10的自然数的和 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(list.stream().reduce(0,(x1,x2)->x1+x2)); System.out.println(list.stream().reduce(10,(x1,x2)->x1+x2)); //reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回Optional<T> //计算公司所有员工工资的和 List<Employee> employeeList = EmployeeData.getEmployees(); System.out.println(employeeList.stream().map(emp -> emp.getSalary()).reduce((salary1, salary2) -> Double.sum(salary1, salary2))); } @Test public void test4(){ //collect(Collector c)——将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中的元素做汇总的方法 //查找工资大于6000的员工,结果返回为一List或Set List<Employee> list = EmployeeData.getEmployees(); List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList()); list1.forEach(System.out::println); //按照员工的年龄进行排序,返回到一个新的List中 System.out.println(); List<Employee> list2 = list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).collect(Collectors.toList()); list2.forEach(System.out::println); } }
总结 1.程序运行中,流Stream只能使用一次,使用后会默认关闭,不能重复使用;重复使用会报错,信息如下:
1 2 3 Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:343) at java.util.stream.Stream.concat(Stream.java:1080)
2.在开发过程中,建议使用Stream.of(list),解决list为null的问题;使用list.stream()之前需要判断list是否为null,避免报错空指针异常(java.lang.NullPointerException)。 3.stream流遍历获取的对象是原对象 4.stream特性思维导图:
> 说明:
java8中有两大最为重要的改变。第一个是Lambda表达式;另外一个则是Stream API。
Stream API(java.util.stream)把真正的函数式编程风格引入java。这是目前为止对java类库最好的补充,因为Stream API可以极大提供java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream是java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用sql执行的数据库查询。也可以使用Stream API并执行操作。简言之,Stream API提供了一种高效且易于使用的处理数据的方式
为什么使用Stream API?
实际开发中,项目中多数数据源来自于Mysql、Oracle等。但现在数据源可以更多了,有mondb,redis等,而这些NoSql的数据就需要jaav层面去处理。
什么是Stream?
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列
Stream和Collection集合区别?
1.Collection是一种静态的内存数据结构,讲的是数据,而Stream是有关计算的,讲的是计算。前者是主要面向内存,存储在内存中,后者主要是面向CPU,通过CPU实现计算。
2.Stream API关注的是多个数据的计算(排序、查找、过滤、映射、遍历等)
3.集合关注的是数据的存储,面向内存的
使用说明?
1.Stream自己不会存储元素。(针对数据计算)
2.Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
3.Stream操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。即一旦执行终止操作,就执行中间操作链,并产生结果。
4.Stream一旦执行了终止操作,就不能再调用其它中间操作或终终止操作了~
操作步骤
Stream的实例化
一系列的中间操作
执行终止操作
数据准备:
创建Employee类
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 package data; import com.oracle.webservices.internal.api.databinding.DatabindingMode; public class Employee { private long id; private String name; private int age; private double salary; public Employee(long id,String name,int age,double salary){ this.id = id; this.age = age; this.name = name; this.salary = salary; } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } @Override public String toString(){ return "Employee{"+"id="+id+",name="+name+",age="+age+",salary="+salary+"}"; } }
创建EmployeeData类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package data; import java.util.ArrayList; import java.util.List; public class EmployeeData { public static List<Employee> getEmployees(){ List<Employee> list = new ArrayList<>(); list.add(new Employee(1001,"马化腾",34,6000.38)); list.add(new Employee(1002,"马云",2,9876.38)); list.add(new Employee(1003,"刘强东",33,3000.82)); list.add(new Employee(1004,"雷军",26,7657.37)); list.add(new Employee(1005,"李彦宏",65,5555.38)); list.add(new Employee(1006,"比尔盖茨",42,9500.38)); list.add(new Employee(1007,"任正非",34,4333.32)); list.add(new Employee(1008,"扎克伯格",35,2500.32)); return list; } }
1.创建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 package streamTest; import data.Employee; import data.EmployeeData; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.stream.IntStream; import java.util.stream.Stream; public class StreamAPITest { //创建Stream方式一:通过集合 @Test public void test1(){ List<Employee> list = EmployeeData.getEmployees(); //返回一个shuixu Stream<Employee> stream = list.stream(); //返回一个并行流 Stream<Employee> stream1 = list.parallelStream(); } //创建Stream方式二:通过数组 @Test public void test2(){ //调用Arrays类的static<T> Stream<T> stream(T[] array):返回一个流 Integer[] arr = new Integer[]{1,2,3,4,5}; Stream<Integer> stream = Arrays.stream(arr); int[] arr1 = new int[]{1,2,3,4,5,6,7}; IntStream stream1 = Arrays.stream(arr1); } //创建Stream方式大三:通过Stream的of() @Test public void text3(){ Stream<String> stream = Stream.of("AA", "BB", "C"); } }
2.一系列的在中间操作 1.筛选与切片
2.映射
3.排序
1.筛选与切片 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 package streamTest; import data.Employee; import data.EmployeeData; import org.junit.jupiter.api.Test; import java.util.List; import java.util.stream.Stream; public class StreamAPITest1 { //1-筛选与切片 @Test public void test(){ //filter(Predicate p)——接收Lambda,从流中排除某些元素 //查询员工表中薪资大于7000的员工信息 List<Employee> list = EmployeeData.getEmployees(); System.out.println(list); Stream<Employee> stream = list.stream(); stream.filter(emp -> emp.getSalary() > 7000).forEach(System.out::println); System.out.println(); //limit(n)——截断流,使其元素不超过给定数量 //错误的。因为stream已经执行了终止操作,就不可以在调用其他的中间操作或终止操作了 // stream.limit(2).forEach(System.out::println); list.stream().filter(emp -> emp.getSalary()>7000).limit(2).forEach(System.out::println); // skip(n) ——跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,返回一个空流。 list.stream().skip(5).forEach(System.out::println); } }
注意:如果stream已经执行了终止操作,不可以在调用其他的中间操作或终止操作
2.映射 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 //2-映射 @Test public void test2() { //map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被映射到每个元素上 //转换为大写 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); //方式一: list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); //方式二: list.stream().map(String::toUpperCase).forEach(System.out::println); //获取员工姓名长度大于3的员工 List<Employee> employees = EmployeeData.getEmployees(); employees.stream().filter(employee -> employee.getName().length()>3).forEach(System.out::println); //获取员工姓名长度大于3的员工的姓名 employees.stream().filter(employee -> employee.getName().length()>3).map(employee -> employee.getName()).forEach(System.out::println); }
3.排序 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 //3-排序 @Test public void test3(){ //sorted()——自然排序 Integer[] arr = new Integer[]{ 342,3,64,46,7,3,54,65,68}; String[] arr1 = new String[]{"GG","DD","MM","SS","JJ"}; Arrays.stream(arr).sorted().forEach(System.out::println);//arr数组并没有因为升序做调整 Arrays.stream(arr1).sorted().forEach(System.out::println);//arr数组并没有因为升序做调整 //因为Employee没有实现Comparable接口,所以报错! // List<Employee> list = EmployeeData.getEmployees(); // list.stream().sorted().forEach(System.out::println); //sorted(Comparator com)——定制排序 List<Employee> list = EmployeeData.getEmployees(); //升序 list.stream().sorted((e1,e2)-> e1.getAge()- e2.getAge()).forEach(System.out::println); System.out.println(); //降序 list.stream().sorted((e1,e2)-> e2.getAge()- e1.getAge()).forEach(System.out::println); //针对字符串从大到小 Arrays.stream(arr1).sorted((s1,s2)->s2.compareTo(s1)).forEach(System.out::println); }
注意:Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
3.终止操作 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 package streamTest; import data.Employee; import data.EmployeeData; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.List; import java.util.Optional; import java.util.stream.Collectors; /** * 终止操作 */ public class StreamAPITest2 { //1-匹配与查找 @Test public void test(){ //是否所有的员工的年龄都大于18 //allMatch(Predicate p)——检查是否匹配所有元素 List<Employee> list = EmployeeData.getEmployees(); System.out.println(list.stream().allMatch(emp -> emp.getAge() > 18 )); //是否存在年龄大于18岁的员工 //anyMatch(Predicate p)——检查是否至少匹配一个元素 System.out.println(list.stream().anyMatch(emp -> emp.getAge() > 18)); //是否存在员工的工资大于10000 System.out.println(list.stream().anyMatch(emp -> emp.getSalary() > 10000)); //findFirst——返回第一个元素 System.out.println(list.stream().findFirst().get()); } @Test public void test2(){ List<Employee> list = EmployeeData.getEmployees(); System.out.println(list.stream().filter(employee -> employee.getSalary()>7000).count()); //max(Comparator c)——返回流中的最大值 //返回最高工资的员工 System.out.println(list.stream().max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))); //返回最低的工资 //方式一: System.out.println(list.stream().max((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary())).get().getSalary()); //方式二: System.out.println(list.stream().map(employee -> employee.getSalary()).max((e1,e2)->Double.compare(e1, e2)).get()); //min(Comparator c)——返回流中的最小值 //返回最低工资的员工 System.out.println(list.stream().min((e1,e2)->Double.compare(e1.getSalary(), e2.getSalary()))); //forEach(Consumer c)——内部迭代 list.stream().forEach(System.out::println); //针对于集合,jdk8中增加了一个遍历的方法 list.forEach(System.out::println); } //归约 @Test public void test3(){ //reduce(T identity,BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回T //计算1-10的自然数的和 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); System.out.println(list.stream().reduce(0,(x1,x2)->x1+x2)); System.out.println(list.stream().reduce(10,(x1,x2)->x1+x2)); //reduce(BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回Optional<T> //计算公司所有员工工资的和 List<Employee> employeeList = EmployeeData.getEmployees(); System.out.println(employeeList.stream().map(emp -> emp.getSalary()).reduce((salary1, salary2) -> Double.sum(salary1, salary2))); } @Test public void test4(){ //collect(Collector c)——将流转换为其他形式。接受一个Collector接口的实现,用于给Stream中的元素做汇总的方法 //查找工资大于6000的员工,结果返回为一List或Set List<Employee> list = EmployeeData.getEmployees(); List<Employee> list1 = list.stream().filter(emp -> emp.getSalary() > 6000).collect(Collectors.toList()); list1.forEach(System.out::println); //按照员工的年龄进行排序,返回到一个新的List中 System.out.println(); List<Employee> list2 = list.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).collect(Collectors.toList()); list2.forEach(System.out::println); } }
总结 1.程序运行中,流Stream只能使用一次,使用后会默认关闭,不能重复使用;重复使用会报错,信息如下:
1 2 3 Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed at java.util.stream.AbstractPipeline.spliterator(AbstractPipeline.java:343) at java.util.stream.Stream.concat(Stream.java:1080)
2.在开发过程中,建议使用Stream.of(list),解决list为null的问题;使用list.stream()之前需要判断list是否为null,避免报错空指针异常(java.lang.NullPointerException)。 3.stream流遍历获取的对象是原对象 4.stream特性思维导图: