Stream API

Stream API执行流程

Stream API(java.util.stream)把真正的函数式编程风格引入到Java中,可以极大地提高程序员生产力,让程序员写出高效、简洁的代码

实际开发中项目中多数数据源都是来自MySQL、Oracle等关系型数据库,还有部分来自MongDB、Redis等非关系型数据库

  • 当我们从关系型数据库中查询数据时,可以先使用SQL语句在数据库中就对数据进行过滤,排序等操作,最后后台Java程序接收
  • 当我们从非关系型数据库中查询数据时,由于不能在数据库中使用SQL语句操作数据,此时只能靠后台Java程序(Stream API)操作数据库中查询到的数据

Stream是Java8中处理集合的关键抽象概念,它可以指定你希望对集合/数组进行的操作(可是是非常复杂的查找、过滤和映射数据等操作)

  • 使用Stream API对内存数据(集合/数组)进行操作就类似于使用SQL语句对数据库表中数据的操作,即Stream API提供了一种高效且易于使用的处理数据的方式

Stream和Collection集合的区别: Stream关注的是数据的运算(过滤操作)与CPU打交道, 集合关注的是数据的存储(静态的存储结构)与内存打交道

Stream的特性

  • Stream自己不会存储元素
  • Stream不会改变源对象,相反他们会返回一个持有结果的新Stream
  • Stream操作是延迟执行的,只有执行了终止操作后才会执行中间操作链并产生结果
  • Stream一旦执行了终止操作,就不能再次使用该Stream执行其它中间操作或终止操作了,得重新创建一个新的流才行

Stream API_第1张图片

数据准备

public class Employee {

    private int id;
    private String name;
    private int age;
    private double salary;

    public int getId() {
        return id;
    }

    public void setId(int 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;
    }

    public Employee() {
        System.out.println("Employee().....");
    }

    public Employee(int id) {
        this.id = id;
        System.out.println("Employee(int id).....");
    }

    public Employee(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public Employee(int id, String name, int age, double salary) {

        this.id = id;
        this.name = name;
        this.age = age;
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", age=" + age + ", salary=" + salary + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        Employee employee = (Employee) o;

        if (id != employee.id)
            return false;
        if (age != employee.age)
            return false;
        if (Double.compare(employee.salary, salary) != 0)
            return false;
        return name != null ? name.equals(employee.name) : employee.name == null;
    }

    @Override
    public int hashCode() {
        int result;
        long temp;
        result = id;
        result = 31 * result + (name != null ? name.hashCode() : 0);
        result = 31 * result + age;
        temp = Double.doubleToLongBits(salary);
        result = 31 * result + (int) (temp ^ (temp >>> 32));
        return result;
    }
}

提供用于测试数据的类

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, "马云", 12, 9876.12));
        list.add(new Employee(1003, "刘强东", 33, 3000.82));
        list.add(new Employee(1004, "雷军", 26, 7657.37));
        list.add(new Employee(1005, "李彦宏", 65, 5555.32));
        list.add(new Employee(1006, "比尔盖茨", 42, 9500.43));
        list.add(new Employee(1007, "任正非", 26, 4333.32));
        list.add(new Employee(1008, "扎克伯格", 35, 2500.32));

        return list;
    }
}

通过一个数据源创建Stream

方式一通过集合:Java8中的Collection接口扩展了两个获取Stream流的方法

方法名 功能
default Stream stream() 返回一个顺序流(按照顺序操作元素)
default Stream parallelStream() 返回一个并行流(并发的操作元素)
@Test
public void test01(){
    List<Integer> list = Arrays.asList(1,2,3,4,5);
    // 返回一个顺序流
    Stream<Integer> stream1 = list.stream();
    // 返回一个并行流
    Stream<Integer> stream2 = list.parallelStream();
    
    List<Employee> employees = EmployeeData.getEmployees();
    // 返回一个顺序流
    Stream<Employee> stream = employees.stream();
    // 返回一个并行流
    Stream<Employee> employeeStream = employees.parallelStream();
}

方式二通过数组: Java8中的Arrays的静态方法stream()可以获取对应类型的Stream流

方法名 功能
static Stream stream(T[] array) 返回一个自定义类型的Stream流
public static IntStream stream(int[] array) 返回一个int类型的Stream流
public static LongStream stream(long[] array) 返回一个long类型的Stream流
public static DoubleStream stream(double[] array) 返回一个double类型的Stream流
@Test
public void test(){
    // 获取基本数据类型的Stream流
    int[] arr = {1,2,3,4,5};
    IntStream stream = Arrays.stream(arr);
    
    // 获取引用数据类型的Stram流
    String[] arr = {"hello","world"};
    Stream<String> stream = Arrays.stream(arr); 
    
    // 获取自定义类型的Stream流
    Employee kyle = new Employee(9527, "Kyle");
    Employee lucy = new Employee(9421, "Lucy");
    Employee[] employees = {kyle, lucy};
    Stream<Employee> stream1 = Arrays.stream(employees);
    
    
} 

方式三通过指定具体数据: 调用Stream类静态方法of()创建一个流

方法名 功能
public static Stream of(T… values) 通过手动指定任意个数据创建一个流
@Test
public void test04(){
    Stream<Integer> stream = Stream.of(1,2,3,4,5);
    stream.forEach(System.out::println);
}

方式四调用Stream类的静态方法iterate()和generate()创建无限流(了解)

方法名 功能
public static Stream iterate(final T seed, final UnaryOperator f) 迭代
public static Stream generate(Supplier s) 生成
@Test
public void test() {
    // 从0开始迭代遍历10个数,没有limit限制会无限迭代
    Stream.iterate(0, t -> t + 1).limit(10).forEach(System.out::println);

    // 生成10个随机数,没有limit限制会无限生成
    Stream.generate(Math::random).limit(10).forEach(System.out::println);
}

中间操作筛选与切片

中间操作就是处理数据的具体操作,每次处理都会返回一个持有结果的新Stream,即中间操作的方法返回值仍然是Stream类型的对象

  • 中间操作可以是个操作链可对数据源的数据进行n次处理(多个中间操作),但是所有的中间操作只有在执行终止操作时才会一次性全部处理(惰性求值)
  • Stream流一旦执行了终止操作,就不能再次使用该Stream执行其它中间操作,需要根据数据源重新创建一个新的Stream流
方 法 描 述
filter(Predicatep) 接收Lambda表达式然后从流中排除某些元素
distinct() 筛选通过流所生成元素的 hashCode() 和 equals() 去除重复元素
limit(long maxSize) 截断流使其元素不超过给定数量
skip(long n) 跳过元素返回一个扔掉了前 n个元素的流
若流中元素不足n个则返回一个空流,与 limit(n) 互补
@Test
public void test() {
    List<Employee> employees = EmployeeData.getEmployees();
    //1. 查询工资大于7000的员工信息
    employees.stream().filter(employee -> employee.getSalary() > 7000).forEach(System.out::println);
    System.out.println("----------------------------");
    //2. 只输出3条员工信息
    employees.stream().limit(3).forEach(System.out::println);
    System.out.println("----------------------------");
    //3. 跳过前3个元素
    employees.stream().skip(3).forEach(System.out::println);
    System.out.println("----------------------------");
    //4. 通过流所生成元素的hashCode和equals方法去除重复元素
    employees.add(new Employee(9527, "Kyle", 20, 9999));
    employees.add(new Employee(9527, "Kyle", 20, 9999));
    employees.stream().distinct().forEach(System.out::println);
}
/*
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
----------------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
----------------------------
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
----------------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=9527, name='Kyle', age=20, salary=9999.0}
*/

中间操作映射新的Stream

方法 描述
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上并将其映射成一个新的元素(将元素转换成其他形式或提取信息)
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 DoubleStream
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 IntStream
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上产生一个新的 LongStream
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流然后把所有流连接成一个流,

map和flatMap的区别: map方法最终会得到一个含有多个Stream实例的Stream,flatMap最终会得到一个含有多个元素的Stream

@Test
public void test() {
    ArrayList list1 = new ArrayList();
    list1.add(1);
    list1.add(2);
    list1.add(3);
    
    ArrayList list2 = new ArrayList();
    list2.add(1);
    list2.add(2);
    list2.add(3);
	// 集合中有4个元素,[1,2,3,[4,5,6]],类似map
    list1.add(list2);
    // 集合中有6个元素,[1,2,3,4,5,6],类似flatMap
    list1.addAll(list2);
    System.out.println(list1);
}
public class LambdaTest{
    @Test
    public void test() {
        List<String> strings = Arrays.asList("aa", "bb", "cc", "dd");
        List<Employee> employees = EmployeeData.getEmployees();
        // 将集合所有元素转为大写并输出
        strings.stream().map(s -> s.toUpperCase()).forEach(System.out::println);
        System.out.println("--------------------------");
        // 获取员工姓名长度大于3的员工的姓名
        employees.stream().map(Employee::getName).
            // 过滤出name长度>3的员工
            filter(name -> name.length() > 3).
            // 遍历输出
            forEach(System.out::println);
        System.out.println("--------------------------");
        // 使用map将字符串中的多个字符构成的集合转换为对应的Stream实例,然后再获取每一个Stream中的元素
        strings.stream().map(LambdaTest::formStringToStream).
            forEach(characterStream -> characterStream.forEach(System.out::println));
        // 使用flatMap可以直接获取Stream中Stream实例中的元素
        System.out.println("--------------------------");
        strings.stream().flatMap(LambdaTest::formStringToStream).forEach(System.out::println);
    }

    public static Stream<Character> formStringToStream(String str) {
        ArrayList<Character> list = new ArrayList<>();
        for (char c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }

}
/*
AA
BB
CC
DD
--------------------------
比尔盖茨
扎克伯格
--------------------------
a
a
b
b
c
c
d
d
--------------------------
*/

中间操作排序

方法 描述
sorted() 产生一个新流其中按自然顺序排序
sorted(Comparator com) 产生一个新流其中按比较器顺序排序
@Test
public void test20() {
    List<Integer> nums = Arrays.asList(13, 54, 97, 52, 43, 64, 27);
    List<Employee> employees = EmployeeData.getEmployees();
    //自然排序,如果要对employees自然排序就需要Employee类实现Comparable接口
    nums.stream().sorted().forEach(System.out::println);
    //定制排序,先按照年龄升序排,再按照工资降序排
    employees.stream().sorted((o1, o2) -> {
        int compare = Integer.compare(o1.getAge(), o2.getAge());
        if (compare != 0) {
            return compare;
        } else {
            return -Double.compare(o1.getSalary(), o2.getSalary());
        } 
    }).forEach(System.out::println);
}

/*
13
27
43
52
54
64
97
------------------
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
*/

终止操作匹配与查找

终端操作会从流的流水线生成结果,方法返回值类型就不再是Stream了可以是任何不是流的值(如List,Integer,void等)

  • 流进行了终止操作后不能再次使用执行其他中间操作或终止操作
方法 描述
allMatch(Predicate p) 检查是否匹配所有元素
anyMatch(Predicate p) 检查是否至少匹配一个元素
noneMatch(Predicate p) 检查是否没有匹配所有元素
findFirst() 返回第一个元素
findAny() 返回当前流中的任意元素,顺序流会取第一个元素
count() 返回流中元素总数
max(Comparator c) 返回流中最大值
min(Comparator c) 返回流中最小值
forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。
相反,Stream API 使用内部迭代——它帮你把迭代做了)
@Test
public void test21(){
    List<Employee> employees = EmployeeData.getEmployees();
    // 练习:是否所有的员工的工资是否都大于5000
    System.out.println("是否所有的员工的工资是否都大于5000:"+employees.stream().allMatch(employee -> employee.getSalary() > 5000));
    // 练习:是否存在员工年龄小于15
    System.out.println("是否存在员工年龄小于15:"+employees.stream().anyMatch(employee -> employee.getAge() < 15));
    // 练习:是否不存在员工姓“马”
    System.out.println("是否不存在员工姓马:"+employees.stream().noneMatch(employee -> employee.getName().startsWith("马")));
    // 返回流中的第一个元素
    System.out.println("返回第一个元素:"+employees.stream().findFirst());
    // 返回当前流中的任意元素
    System.out.println("返回当前流中的任意元素"+employees.stream().findAny());
    // 返回流中元素的总个数(返回总个数前可以先过滤)
    System.out.println("返回元素总数:"+employees.stream().count());
    // 返回流中最高工资
    System.out.println("返回最高工资:"+employees.stream().map(Employee::getSalary).max(Double::compare));
    // 返回最低工资的员工
    System.out.println("返回最高工资:"+employees.stream().min(e1,e2 -> Double.compare(e1.getSalary(),e2.getSalary()));
    // 返回流中最小值
    System.out.println("返回最小年龄:"+employees.stream().map(Employee::getAge).min(Integer::compare));
    // 内部迭代
    employees.stream().forEach(System.out::println);
    System.out.println("-------------");
    // 使用集合的遍历操作
    employees.forEach(System.out::println);
}

/*
是否所有的员工的工资是否都大于5000:false
是否存在员工年龄小于15:true
是否不存在员工姓马:false
返回第一个元素:Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
返回当前流中的任意元素Optional[Employee{id=1001, name='马化腾', age=34, salary=6000.38}]
返回元素总数:8
返回最高工资:Optional[9876.12]
返回最小年龄:Optional[12]
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
-------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
*/    

终止操作归约

map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名

方法 描述
reduce(T identity, BinaryOperator b) 可以将流中元素反复结合起来返回一个值T
reduce(BinaryOperator b) 可以将流中元素反复结合起来返回一个值Optional
@Test
public void test22() {
    List<Integer> nums = Arrays.asList(13, 32, 23, 31, 94, 20, 77, 21, 17);
    List<Employee> employees = EmployeeData.getEmployees();
    // 练习1:计算1-10的自然数的和(0是初始值)
    System.out.println(nums.stream().reduce(0, Integer::sum));
    // 练习2:手动计算公司所有员工工资总和
    System.out.println(employees.stream().map(Employee::getSalary).reduce((o1, o2) -> o1 + o2));
    // 调用Integer的sum方法计算年龄总和
    System.out.println(employees.stream().map(Employee::getAge).reduce(Integer::sum));
    // 计算公司所有员工工资综合
    Stream<Double> salaryStream = employ.stream.map(Employee::getSalary);
    Optional<Double> sumMoney = salaryStream.reduce(Double::sum);
    System.out.println(sumMoney);
}
/*
328
Optional[48424.08]
Optional[273]
Optional[48424.08]
*/

终止操作收集

方 法 描 述
collect(Collector c) 接收一个Collector接口的实现将流转换为其他形式,用于给Stream中元素做汇总的方法

Collector接口中方法的实现决定了如何对流执行收集的操作(如收集到List、Set、Map),可以方便地创建常见收集器实例

方法 返回类型 作用
toList List 把流中元素收集到List
List emps= list.stream().collect(Collectors.toList());
toSet List 把流中元素收集到List
Set emps= list.stream().collect(Collectors.toSet());
toCollection Collection 把流中元素收集到创建的集合
Collection emps =list.stream().collect(Collectors.toCollection(ArrayList::new));
counting Long 计算流中元素的个数
long count = list.stream().collect(Collectors.counting());
summinglnt Integer 对流中元素的整数属性求和
int total=list.stream().collect(Collectors.summingInt(Employee::getSalary));
averagingInt Double 计算流中元素Integer属性的平均值
double avg = list.stream().collect(Collectors.averagingInt(Employee::getSalary));
summarizinglnt IntSummaryStatistics 收集流中Integer属性的统计值。如:平均值
int SummaryStatisticsiss=list.stream().collect(Collectors.summarizingInt(Employee::getSalary));
joining String 连接流中每个字符串
String str= list.stream().map(Employee::getName).collect(Collectors.joining());
maxBy Optional 根据比较器选择最大值
optionalmax=list.stream().collect(Collectors.maxBy(comparingInt(Employee::getSalary));
minBy Optional 根据比较器选择最小值
Optionalmin = list.stream().collect(Collectors.minBy(comparingInt(Employee::getSalary));
reducing 归约产生的类型 从一个作为累加器的初始值开始,利用BinaryOperator与流中元素逐个结合,从而归约成单个值
int total=list.stream().collect(Collectors.reducing(0, Employe::getSalar, Integer::sum));
collectingAndThen 转换函数返回的类型 包裹另一个收集器,对其结果转换函数
int how= list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
groupingBy Map 根据某属性值对流分组,属性为K,结果为V
Mapmap= list.stream().collect(Collectors.groupingBy(Employee::getStatus));
partitioningBy Map 根据true或false进行分区
Mapvd=list.stream().collect(Collectors.partitioningBy(Employee::getManage));
@Test
public void test23() {
    // 练习1:查找工资大于6000的员工,结果返回为一个List
    List<Employee> employees = EmployeeData.getEmployees();
    List<Employee> list = employees.stream().filter(employee -> employee.getSalary() > 6000).collect(Collectors.toList());
    list.forEach(System.out::println);
    System.out.println("--------------------");
    // 练习2:查找年龄大于20的员工,结果返回为一个List
    employees.add(new Employee(9527,"Kyle",21,9999));
    employees.add(new Employee(9527,"Kyle",21,9999));
    Set<Employee> set = employees.stream().filter(employee -> employee.getAge() > 20).collect(Collectors.toSet());
    set.forEach(System.out::println);
}

/*
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1002, name='马云', age=12, salary=9876.12}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
--------------------
Employee{id=1001, name='马化腾', age=34, salary=6000.38}
Employee{id=1007, name='任正非', age=26, salary=4333.32}
Employee{id=1008, name='扎克伯格', age=35, salary=2500.32}
Employee{id=1006, name='比尔盖茨', age=42, salary=9500.43}
Employee{id=1005, name='李彦宏', age=65, salary=5555.32}
Employee{id=1003, name='刘强东', age=33, salary=3000.82}
Employee{id=9527, name='Kyle', age=21, salary=9999.0}
Employee{id=1004, name='雷军', age=26, salary=7657.37}
*/

Optional类

空指针异常是导致Java应用程序失败的最常见原因,以前为了解决空指针异常Google公司著名的Guava项目引入了Optional类(通过检查空值的方式来防止代码污染)

受到Google Guava的启发Java 8也引入了java.util.Optional类,Optional类是一个可以为null的容器对象

  • Optional类可以保存类型T的值代表这个值存在
  • 以前用null表示一个值不存在,现在Optional类保存null表示这个值不存在可以避免空指针异常

创建Optional类

创建Optional类对象的方法

方法名 功能
Optional.of(T t) 创建一个Optional实例,t必须非空
Optional.empty() 创建一个空的Optional实例
Optional.ofNullable(T t) t可以为null

准备实体类Boy和Girl

public class Boy {
    private Girl girl;
    public Boy() {
    }

    public Boy(Girl girl) {
        this.girl = girl;
    }
    @Override
    public String toString() {
        return "Boy{" +
                "girl=" + girl +
                '}';
    }
}
public class Girl {
    private String name;
    public Girl() {
    }

    public Girl(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Girl{" +
                "name='" + name + '\'' +
                '}';
    }
}
@Test
public void test(){
    Girl girl = new Girl();
    // 如果girl等于null会报空指针异常
    Optional<Girl> optionalGirl = Optional.of(girl);
}

@Test
public void test25(){
    Girl girl = new Girl();
    girl = null;
    // girl可以为null
    Optional<Girl> optionalGirl = Optional.ofNullable(girl);
    // girl为null输出Optional.empty,girl不为null输出Optional[Girl{name="null"}]
    System.out.println(optionalGirl);
    
    // girl为null输出Girl{name="赵丽颖"},girl不为null输出Girl{name="null"}
    Girl girl1 = optionalGirl.orElse(new Girl("赵丽颖"));
    System.out.println(girl1);
}

使用Optional

判断Optional容器中是否包含对象

方法名 功能
boolean isPresent() 判断是否包含对象
void ifPresent(Consumer consumer) 如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它

获取Optional容器的对象

方法名 功能
T get() 如果调用对象包含值返回该值,否则抛异常
T orElse(T other) 如果有值(内部封装的t非空)则将其返回,否则返回指定的other对象
T orElseGet(Supplier other) 如果有值则将其返回,否则返回由Supplier接口实现提供的对象
T orElseThrow(Supplier exceptionSupplier) 如果有值则将其返回,否则抛出由Supplier接口实现提供的异常
@Test
public void test26(){
    Boy boy = new Boy();
    boy = null;
    // 此会出现空指针异常
    String girlName = getGirlName(boy);
    System.out.println(girlName);
}

// 获取女孩的名字,容易出现空指针异常
private String getGirlName(Boy boy) {
    return boy.getGirl().getName();
}
@Test
public void test27(){
    Boy boy = new Boy();
    boy = null;
    String girlName = getGirlName1(boy);
    System.out.println(girlName);
}

// 优化以后的getGirlName():
public String getGirlName1(Boy boy){
    if(boy != null){
        Girl girl = boy.getGirl();
        if(girl != null){
            return girl.getName();
        }
    }
    return null;
}

使用Optional类

@Test
public void test28(){
    // 樱岛麻衣
    Boy boy = null;
    // 喜多川海梦
    boy = new Boy();
    // Lucy
    boy = new Boy(new Girl("Lucy"));
    String girlName = getGirlName2(boy);
    System.out.println(girlName);
}

//使用Optional类的getGirlName()
public String getGirlName2(Boy boy){
    Optional<Boy> boyOptional = Optional.ofNullable(boy);
    //此时的boy1一定非空
    Boy boy1 = boyOptional.orElse(new Boy(new Girl("樱岛麻衣")));
    Girl girl = boy1.getGirl();

    Optional<Girl> girlOptional = Optional.ofNullable(girl);
    //girl1一定非空
    Girl girl1 = girlOptional.orElse(new Girl("喜多川海梦"));
    return girl1.getName();
} 

你可能感兴趣的:(JavaSe,java)