Stream API
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等。但现在数 据源可以更多了,有MongDB,Radis等,而这些NoSQL的数据就需要 Java层面去处理。
-
Stream 和 Collection 集合的区别:Collection 是一种静态的内存数据 结构,而 Stream 是有关计算的。前者是主要面向内存,存储在内存中, 后者主要是面向 CPU,通过 CPU 实现计算。
什么是 Stream
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。 “集合讲的是数据,Stream讲的是计算!”
注意:
1\Stream 自己不会存储元素。
2\Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
3\Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
Stream 的操作三个步骤
1- 创建 :Stream 一个数据源(如:集合、数组),获取一个流
2- 中间操作 :一个中间操作链,对数据源的数据进行处理
3- 终止操作(终端操作) :一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用
创建 Stream
1 /** 2 * 1. Stream关注的是对数据的运算,与CPU打交道 3 * 集合关注的是数据的存储,与内存打交道 4 * 5 * 2. 6 * ①Stream 自己不会存储元素。 7 * ②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。 8 * ③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行 9 * 10 * 3.Stream 执行流程 11 * ① Stream的实例化 12 * ② 一系列的中间操作(过滤、映射、...) 13 * ③ 终止操作 14 * 15 * 4.说明: 16 * 4.1 一个中间操作链,对数据源的数据进行处理 17 * 4.2 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用 18 * 19 * 20 * 测试Stream的实例化 21 * 22 */ 23 public class StreamAPITest { 24 25 //创建 Stream方式一:通过集合 26 @Test 27 public void test1(){ 28 List<Employee> employees = EmployeeData.getEmployees(); 29 30 // default Stream<E> stream() : 返回一个顺序流 31 Stream<Employee> stream = employees.stream(); 32 33 // default Stream<E> parallelStream() : 返回一个并行流 34 Stream<Employee> parallelStream = employees.parallelStream(); 35 } 36 37 //创建 Stream方式二:通过数组 38 @Test 39 public void test2(){ 40 int[] arr = new int[]{1, 2, 3, 4, 5, 6}; 41 //调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流 42 IntStream stream = Arrays.stream(arr); 43 44 Employee e1 = new Employee(1001, "Tom"); 45 Employee e2 = new Employee(1002, "Jerry"); 46 Employee[] arr1 = new Employee[]{e1, e2}; 47 Stream<Employee> stream1 = Arrays.stream(arr1); 48 } 49 50 //创建 Stream方式三:通过Stream的of() 51 @Test 52 public void test3(){ 53 Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6); 54 } 55 56 //创建 Stream方式四:创建无限流 57 @Test 58 public void test4(){ 59 // 迭代 60 // public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) 61 //遍历前10个偶数 62 Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println); 63 64 // 生成 65 // public static<T> Stream<T> generate(Supplier<T> s) 66 Stream.generate(Math::random).limit(10).forEach(System.out::println); 67 68 } 69 }
Stream 的中间操作
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止 操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全 部处理,称为“惰性求值”。
1 /** 2 * 测试Stream的中间操作 3 */ 4 public class StreamAPITest1 { 5 //1-筛选与切片 6 @Test 7 public void test1() { 8 List<Employee> list = EmployeeData.getEmployees(); 9 // filter(Predicate p)——接收 Lambda , 从流中排除某些元素。 10 list.stream().filter(e -> e.getSalary() > 7000).forEach(System.out::println); 11 12 System.out.println(); 13 // limit(n)——截断流,使其元素不超过给定数量。 14 list.stream().limit(3).forEach(System.out::println); 15 16 System.out.println(); 17 // skip(n) —— 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 18 list.stream().skip(5).forEach(System.out::println); 19 20 System.out.println(); 21 // distinct()——筛选,通过流所生成元素的 hashCode() 和 equals() 去除重复元素 22 list.add(new Employee(1010, "刘强东", 40, 8000)); 23 list.add(new Employee(1010, "刘强东", 41, 8000)); 24 list.add(new Employee(1010, "刘强东", 40, 8000)); 25 list.add(new Employee(1010, "刘强东", 40, 8000)); 26 list.add(new Employee(1010, "刘强东", 40, 8000)); 27 list.stream().distinct().forEach(System.out::println); 28 29 30 } 31 32 33 //映射 34 @Test 35 public void test2() { 36 // map(Function f)——接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。 37 List<String> list = Arrays.asList("aa", "bb", "cc", "dd"); 38 list.stream().map(str -> str.toUpperCase()).forEach(System.out::println); 39 40 // 练习1:获取员工姓名长度大于3的员工的姓名。 41 List<Employee> employees = EmployeeData.getEmployees(); 42 Stream<String> namesStream = employees.stream().map(Employee::getName); 43 namesStream.filter(name -> name.length() > 3).forEach(System.out::println); 44 45 //练习2: 46 Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest1::fromStringtoStream); 47 streamStream.forEach(s -> s.forEach(System.out::println)); 48 System.out.println(); 49 // flatMap(Function f)——接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。 50 Stream<Character> characterStream = list.stream().flatMap(StreamAPITest1::fromStringtoStream); 51 characterStream.forEach(System.out::println); 52 } 53 54 //将字符串中的多个字符构成的集合转换为对应的Stream的实例 55 public static Stream<Character> fromStringtoStream(String str) { 56 ArrayList<Character> list = new ArrayList<>(); 57 for (Character c : str.toCharArray()) { 58 list.add(c); 59 } 60 return list.stream(); 61 } 62 63 //3-排序 64 @Test 65 public void test3() { 66 // sorted()——自然排序 67 List<Integer> list = Arrays.asList(12, 43, 65, 34, 87, 0, -98, 7); 68 list.stream().sorted().forEach(System.out::println); 69 //抛异常,原因:Employee没有实现Comparable接口 70 // List<Employee> employees = EmployeeData.getEmployees(); 71 // employees.stream().sorted().forEach(System.out::println); 72 73 // sorted(Comparator com)——定制排序 74 List<Employee> employees = EmployeeData.getEmployees(); 75 employees.stream().sorted((e1, e2) -> { 76 int ageValue = Integer.compare(e1.getAge(), e2.getAge()); 77 if (ageValue != 0) { 78 return ageValue; 79 } else { 80 return Double.compare(e1.getSalary(), e2.getSalary()); 81 } 82 }).forEach(System.out::println); 83 } 84 }
Stream 的终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例 如:List、Integer,甚至是 void 。
流进行了终止操作后,不能再次使用。
1 /** 2 * 测试Stream的终止操作 3 * 4 */ 5 public class StreamAPITest2 { 6 //1-匹配与查找 7 @Test 8 public void test1(){ 9 List<Employee> employees = EmployeeData.getEmployees(); 10 11 // allMatch(Predicate p)——检查是否匹配所有元素。 12 // 练习:是否所有的员工的年龄都大于18 13 boolean allMatch = employees.stream().allMatch(e -> e.getAge() > 18); 14 System.out.println(allMatch); 15 16 // anyMatch(Predicate p)——检查是否至少匹配一个元素。 17 // 练习:是否存在员工的工资大于 10000 18 boolean anyMatch = employees.stream().anyMatch(e -> e.getSalary() > 10000); 19 System.out.println(anyMatch); 20 21 // noneMatch(Predicate p)——检查是否没有匹配的元素。 22 // 练习:是否存在员工姓“雷” 23 boolean noneMatch = employees.stream().noneMatch(e -> e.getName().startsWith("雷")); 24 System.out.println(noneMatch); 25 26 // findFirst——返回第一个元素 27 Optional<Employee> first = employees.stream().findFirst(); 28 System.out.println(first); 29 30 // findAny——返回当前流中的任意元素 31 Optional<Employee> any = employees.parallelStream().findAny(); 32 System.out.println(any); 33 } 34 35 @Test 36 public void test2(){ 37 List<Employee> employees = EmployeeData.getEmployees(); 38 39 // count——返回流中元素的总个数 40 long count = employees.stream().count(); 41 System.out.println(count); 42 43 // max(Comparator c)——返回流中最大值 44 // 练习:返回最高的工资: 45 Stream<Double> salaryStream = employees.stream().map(employee -> employee.getSalary()); 46 Optional<Double> max = salaryStream.max(Double::compareTo); 47 System.out.println(max); 48 49 // min(Comparator c)——返回流中最小值 50 // 练习:返回最低工资的员工 51 Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary())); 52 System.out.println(min); 53 System.out.println(); 54 55 56 // forEach(Consumer c)——内部迭代 57 employees.stream().forEach(System.out::println); 58 System.out.println(); 59 60 //使用集合的遍历操作 61 employees.forEach(System.out::println); 62 } 63 64 //2-归约 65 @Test 66 public void test3(){ 67 68 // reduce(T identity, BinaryOperator)——可以将流中元素反复结合起来,得到一个值。返回 T 69 // 练习1:计算1-10的自然数的和 70 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 71 Integer sum = list.stream().reduce(0, Integer::sum); 72 System.out.println(sum); 73 74 // reduce(BinaryOperator) ——可以将流中元素反复结合起来,得到一个值。返回 Optional<T> 75 // 练习2:计算公司所有员工工资的总和 76 List<Employee> employees = EmployeeData.getEmployees(); 77 Stream<Double> salaryStream = employees.stream().map(Employee::getSalary); 78 Optional<Double> sumMoney = salaryStream.reduce(Double::sum); 79 System.out.println(sumMoney); 80 } 81 82 //3-收集 83 @Test 84 public void test4() { 85 // collect(Collector c)——将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法 86 // 练习1:查找工资大于6000的员工,结果返回为一个List或Set 87 88 List<Employee> employees = EmployeeData.getEmployees(); 89 List<Employee> employeeList = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toList()); 90 employeeList.forEach(System.out::println); 91 System.out.println(); 92 93 Set<Employee> employeeSet = employees.stream().filter(e -> e.getSalary() > 6000).collect(Collectors.toSet()); 94 employeeSet.forEach(System.out::println); 95 } 96 }
Optional类
-
Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
-
创建Optional类对象的方法:
- Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
- Optional.empty() : 创建一个空的 Optional 实例
- Optional.ofNullable(T t):t可以为null
-
判断Optional容器中是否包含对象:
-
boolean isPresent() : 判断是否包含对象
-
void ifPresent(Consumer<? super T> consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
-
- 获取Optional容器的对象:
-
-
T get(): 如果调用对象包含值,返回该值,否则抛异常
-
T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
-
T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由
Supplier接口实现提供的对象。
-
T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
-
Option类代码举例
1 public class Boy { 2 private Girl girl; 3 4 @Override 5 public String toString() { 6 return "Boy{" + 7 "girl=" + girl + 8 '}'; 9 } 10 11 public Girl getGirl() { 12 return girl; 13 } 14 15 public void setGirl(Girl girl) { 16 this.girl = girl; 17 } 18 19 public Boy() { 20 21 } 22 23 public Boy(Girl girl) { 24 25 this.girl = girl; 26 } 27 }
1 public class Girl { 2 3 private String name; 4 5 @Override 6 public String toString() { 7 return "Girl{" + 8 "name='" + name + '\'' + 9 '}'; 10 } 11 12 public String getName() { 13 return name; 14 } 15 16 public void setName(String name) { 17 this.name = name; 18 } 19 20 public Girl() { 21 22 } 23 24 public Girl(String name) { 25 26 this.name = name; 27 } 28 }
1 /** 2 * Optional类:为了在程序中避免出现空指针异常而创建的。 3 * 4 * 常用的方法:ofNullable(T t) 5 * orElse(T t) 6 * 7 */ 8 public class OptionalTest { 9 10 /* 11 Optional.of(T t) : 创建一个 Optional 实例,t必须非空; 12 Optional.empty() : 创建一个空的 Optional 实例 13 Optional.ofNullable(T t):t可以为null 14 15 */ 16 @Test 17 public void test1(){ 18 Girl girl = new Girl(); 19 // girl = null; 20 //of(T t):保证t是非空的 21 Optional<Girl> optionalGirl = Optional.of(girl); 22 23 System.out.println(optionalGirl.get()); 24 } 25 26 @Test 27 public void test2(){ 28 Girl girl = new Girl(); 29 // girl = null; 30 //ofNullable(T t):t可以为null 31 Optional<Girl> optionalGirl = Optional.ofNullable(girl); 32 System.out.println(optionalGirl); 33 //orElse(T t1):如果单前的Optional内部封装的t是非空的,则返回内部的t. 34 //如果内部的t是空的,则返回orElse()方法中的参数t1. 35 Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖")); 36 System.out.println(girl1); 37 38 } 39 40 41 public String getGirlName(Boy boy){ 42 return boy.getGirl().getName(); 43 } 44 45 @Test 46 public void test3(){ 47 Boy boy = new Boy(); 48 boy = null; 49 String girlName = getGirlName(boy); 50 System.out.println(girlName); 51 52 } 53 //优化以后的getGirlName(): 54 public String getGirlName1(Boy boy){ 55 if(boy != null){ 56 Girl girl = boy.getGirl(); 57 if(girl != null){ 58 return girl.getName(); 59 } 60 } 61 62 return null; 63 64 } 65 66 @Test 67 public void test4(){ 68 Boy boy = new Boy(); 69 boy = null; 70 String girlName = getGirlName1(boy); 71 System.out.println(girlName); 72 73 } 74 75 //使用Optional类的getGirlName(): 76 public String getGirlName2(Boy boy){ 77 78 Optional<Boy> boyOptional = Optional.ofNullable(boy); 79 //此时的boy1一定非空 80 Boy boy1 = boyOptional.orElse(new Boy(new Girl("迪丽热巴"))); 81 82 Girl girl = boy1.getGirl(); 83 84 Optional<Girl> girlOptional = Optional.ofNullable(girl); 85 //girl1一定非空 86 Girl girl1 = girlOptional.orElse(new Girl("古力娜扎")); 87 88 return girl1.getName(); 89 } 90 91 @Test 92 public void test5(){ 93 Boy boy = null; 94 boy = new Boy(); 95 boy = new Boy(new Girl("苍老师")); 96 String girlName = getGirlName2(boy); 97 System.out.println(girlName); 98 99 } 100 101 102 103 }