第一章:函数式编程思想

1.1 概述

函数式编程思想概述.png

  • 在数学中,函数(y = f(x))就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。编程中的函数,也有类似的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你一个返回结果。对于调用者来说,关注的是这个方法具备什么样的功能。相对而言,面向对象过分强调“必须通过对象的形式来做事情”,而函数式思想则尽量忽略面向对象的复杂语法— 强调做什么,而不是以什么形式做
  • 面向对象思想:做一件事情,找一个能解决这个事情的对象,调用对象的方法,完成事情。
  • 函数式编程思想:只要能获取到结果,谁去做不重要,重视的是结果,不是过程。
  • JDK 8 引入了 Lambda 表示式之后,Java 也开始支持函数式编程。
  • Lambda 表达式不是 Java 最早使用的,很多语言支持很早就支持 Lambda ,例如:C++ 、C# 、Python 、Scala 等。如果有 JavaScript 或 Python 的语言基础,对理解 Lambda 表达式有很大的帮助,可以说 Lambda 表达式其实就是实现 SAM(Single Abstract Method,即接口中只能有一个抽象方法)接口的语法糖,使得 Java 也算是支持函数式编程的语言。Lambda 写得好可以极大的减少代码冗余,同时可读性也好过冗余的匿名内部类。

    注意:

    • “语法糖”是指使用更加方便,但是原理不变的代码语法。例如在遍历集合时使用的 for-each 语法,其实
      底层的实现原理仍然是迭代器,这便是“语法糖”。
    • 从应用层面来讲,Java 中的 Lambda 可以被当做是匿名内部类的“语法糖”,但是二者在原理上是不同的。

1.2 冗余的匿名内部类

  • 当需要启动一个线程去完成任务的时候,通过会通过 java.lang.Runnable 接口来定义任务内容,并使用 java.lang.Thread 类来启动该线程,代码如下:
  1. package com.github.demo1;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2021-10-09 09:09
  6. */
  7. public class Test {
  8. public static void main(String[] args) {
  9. // 匿名内部类
  10. Runnable runnable = new Runnable() {
  11. @Override
  12. public void run() {
  13. System.out.println("多线程任务执行!!!!");
  14. }
  15. };
  16. // 启动线程
  17. new Thread(runnable).start();
  18. }
  19. }
  • 本着“一切皆对象”的思想,这样的做法无可厚非的:首先创建 Runnable 接口的匿名内部类对象来指定任务内容,然后将其交给一个线程来启动。
  • 代码分析:
  • 对于Runnable 的匿名内部类的用法,可以分析出以下几点:
    • Thread 类需要 Runnable 接口作为参数,其中的抽象 run 方法是用来指定线程任务内容的核心。
    • ② 为了指定 run 的方法体,不得不 需要 Runnable 接口的实现类。
    • ③ 为了省去定义一个 Runnable 实现类的麻烦,不得不 实现匿名内部类。
    • ④ 为了覆盖重写抽象 run 方法,其方法名称、方法参数、方法的返回值 不得不 重写一遍,且不能写错。
  • 但是,实际上,方法体才是关键所在

1.3 编程思想的转换

  • 面向对象的编程思想:关注的是怎么做,由谁做?
  • 函数式编程思想:更多关注的是做什么?

  • 我们真的希望创建一个匿名内部类对象吗?当然不,我们只是为了完成这件事情(语法的限制)不得不 创建一个对象。我们真正希望做的事情是:将 run 方法体内的代码传递给 Thread 类知晓。

  • 传递一段代码 — 这才是我们真正的目的。而创建对象只是受限于面向对象的语法而不得不采用的一种手段方式。那么,有没有更加简单的办法?如果我们将关注点从 怎么做,由谁做? 回归到 做什么 的本质上,就会发现只要能够更好的达成目的,过程和形式并不重要(成王败寇)。

  • 生活举例:

  • 当我们需要从北京到上海的时候,可以选择高铁、汽车、骑行甚至徒步。我们真正的目的是到达上海,而如何到达上海的形式并不重要,所以我们一直在探索有没有比高铁更好的方式—搭乘飞机。

交通方式.png

  • 而现在这种飞机(甚至是飞船)已经诞生。

Lambda.png

  • 2014 年 3 月,Oracle 发布的 JDK 8,加入了 Lambda 表达式的重量级新特性,为我们打开了新世界的大门。

1.4 体验 Lambda 的优雅写法

  • 借助 JDK 8 的全新语法,上述 Runnable 接口的匿名内部类写法可以通过更加简单的 Lambda 表达式达到同等效果:
  1. package com.github.demo2;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2021-10-09 09:33
  6. */
  7. public class Test {
  8. public static void main(String[] args) {
  9. new Thread(() -> System.out.println("多线程任务执行!!!!")).start();
  10. }
  11. }
  • 这段代码的执行效果和匿名内部类写法的执行效果一致,可以在 JDK 8 及其以上版本编译通过。从代码的语义可以看出:我们启动的一个线程,而线程的任务内容以一种更加简洁的形式被指定。
  • 不再有 不得不创建接口对象 的束缚,不再有 抽象方法覆盖重写 的负担,就是这么

第二章:Lambda 表达式

2.1 函数式接口

2.1.1 概述

  • Lambda 表达式其实就是实现 SAM(Single Abstract Method,即该接口中有且仅有一个抽象方法,当然该接口中还可以包含非抽象方法)接口的语法糖。
  • 其实,只要满足 SAM 特征的接口就是函数式接口,就可以使用 Lambda 表达式了,但是如果在声明函数式接口的时候,使用 @FunctionalInterface 注解来声明,那么编译器就会强制检查该接口是否确实有且仅有一个抽象方法,如果不是,将报错。
  • 之前学过的SAM接口中,标记了 @FunctionalInterface 注解的接口有:Runnable、Comparator、FileFilter。
  • JDK 8 在 java.util.function 包中新增了很多函数式接口,主要分为消费型、供给型、断言型(判断型)、函数型(功能型)。基本上可以满足我们实际的开发,当然,我们也可以自定义函数式接口。

2.1.2 自定义函数式接口

  • 语法:
  1. @FunctionalInterface
  2. 修饰符 interface 接口名{
  3. [public abstract] 返回值类型 方法名(形参列表);
  4. }

注意:在接口中的抽象方法,可以省略 public abstract 关键字的。

  • 示例:
  1. package com.github.demo3;
  2. /**
  3. * 计算
  4. *
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2021-10-09 11:12
  8. */
  9. @FunctionalInterface
  10. public interface Calculator {
  11. int calc(int a, int b);
  12. }
  1. package com.github.demo3;
  2. /**
  3. * @author 许大仙
  4. * @version 1.0
  5. * @since 2021-10-09 10:44
  6. */
  7. public class Test {
  8. public static void main(String[] args) {
  9. calc(1, 2, (a, b) -> a + b); // result = 3
  10. calc(1, 2, (a, b) -> a - b); // result = -1
  11. calc(1, 2, (a, b) -> a * b); // result = 2
  12. calc(1, 2, (a, b) -> a / b); // result = 0
  13. calc(1, 2, (a, b) -> a % b); // result = 1
  14. }
  15. public static void calc(int a, int b, Calculator calc) {
  16. int result = calc.calc(a, b);
  17. System.out.println("result = " + result);
  18. }
  19. }

2.1.3 函数式接口的分类

  • 除了我们可以自定义函数式接口外,JDK 还给我们内置了一些函数式接口,具体分类如下。

2.1.3.1 消费型接口

  • 消费型接口的抽象方法的特点:有形参,但是返回值类型是 void 。 | 接口名 | 抽象方法 | 描述 | | —- | —- | —- | | Consumer | void accept(T t) | 接收一个对象用于完成功能 | | BiConsumer | void accept(T t, U u) | 接收两个对象用于完成功能 | | DoubleConsumer | void accept(double value) | 接收一个double值 | | IntConsumer | void accept(int value) | 接收一个int值 | | LongConsumer | void accept(long value) | 接收一个long值 | | ObjDoubleConsumer | void accept(T t, double value) | 接收一个对象和一个double值 | | ObjIntConsumer | void accept(T t, int value) | 接收一个对象和一个int值 | | ObjLongConsumer | void accept(T t, long value) | 接收一个对象和一个long值 |

2.1.3.2 供给型接口

  • 供给型接口的抽象方法的特点:无形参,但是有返回值。 | 接口名 | 抽象方法 | 描述 | | —- | —- | —- | | Supplier | T get() | 返回一个对象 | | BooleanSupplier | boolean getAsBoolean() | 返回一个boolean值 | | DoubleSupplier | double getAsDouble() | 返回一个double值 | | IntSupplier | int getAsInt() | 返回一个int值 | | LongSupplier | long getAsLong() | 返回一个long值 |

2.1.3.3 断言型接口

  • 断言型接口的特点:有形参,但是返回值类型是 boolean 。 | 接口名 | 抽象方法 | 描述 | | —- | —- | —- | | Predicate | boolean test(T t) | 接收一个对象 | | BiPredicate | boolean test(T t, U u) | 接收两个对象 | | DoublePredicate | boolean test(double value) | 接收一个double值 | | IntPredicate | boolean test(int value) | 接收一个int值 | | LongPredicate | boolean test(long value) | 接收一个long值 |

2.3.3.4 函数型接口

  • 函数型接口的特点:有形参,有返回值。 | 接口名 | 抽象方法 | 描述 | | —- | —- | —- | | Function | R apply(T t) | 接收一个T类型对象,返回一个R类型对象结果 | | UnaryOperator | T apply(T t) | 接收一个T类型对象,返回一个T类型对象结果 | | DoubleFunction | R apply(double value) | 接收一个double值,返回一个R类型对象 | | IntFunction | R apply(int value) | 接收一个int值,返回一个R类型对象 | | LongFunction | R apply(long value) | 接收一个long值,返回一个R类型对象 | | ToDoubleFunction | double applyAsDouble(T value) | 接收一个T类型对象,返回一个double | | ToIntFunction | int applyAsInt(T value) | 接收一个T类型对象,返回一个int | | ToLongFunction | long applyAsLong(T value) | 接收一个T类型对象,返回一个long | | DoubleToIntFunction | int applyAsInt(double value) | 接收一个double值,返回一个int结果 | | DoubleToLongFunction | long applyAsLong(double value) | 接收一个double值,返回一个long结果 | | IntToDoubleFunction | double applyAsDouble(int value) | 接收一个int值,返回一个double结果 | | IntToLongFunction | long applyAsLong(int value) | 接收一个int值,返回一个long结果 | | LongToDoubleFunction | double applyAsDouble(long value) | 接收一个long值,返回一个double结果 | | LongToIntFunction | int applyAsInt(long value) | 接收一个long值,返回一个int结果 | | DoubleUnaryOperator | double applyAsDouble(double operand) | 接收一个double值,返回一个double | | IntUnaryOperator | int applyAsInt(int operand) | 接收一个int值,返回一个int结果 | | LongUnaryOperator | long applyAsLong(long operand) | 接收一个long值,返回一个long结果 | | BiFunction | R apply(T t, U u) | 接收一个T类型和一个U类型对象,返回一个R类型对象结果 | | BinaryOperator | T apply(T t, T u) | 接收两个T类型对象,返回一个T类型对象结果 | | ToDoubleBiFunction | double applyAsDouble(T t, U u) | 接收一个T类型和一个U类型对象,返回一个double | | ToIntBiFunction | int applyAsInt(T t, U u) | 接收一个T类型和一个U类型对象,返回一个int | | ToLongBiFunction | long applyAsLong(T t, U u) | 接收一个T类型和一个U类型对象,返回一个long | | DoubleBinaryOperator | double applyAsDouble(double left, double right) | 接收两个double值,返回一个double结果 | | IntBinaryOperator | int applyAsInt(int left, int right) | 接收两个int值,返回一个int结果 | | LongBinaryOperator | long applyAsLong(long left, long right) | 接收两个long值,返回一个long结果 |

2.2 Lambda 表达式的语法

  • Lambda 表达式是用来给 函数式接口 的变量或形参赋值使用的。
  • 本质上,Lambda 表达式是用于实现 函数式接口抽象方法
  • 语法:

    1. (形参列表) -> {Lambda体}
  • 说明:

    • (形参列表):就是要赋值的函数式接口的抽象方法的 (形参列表)
    • {Lambda体}:就是实现这个抽象方法的方法体。
    • ->:Lambda操作符。
  • 优化:

    • {Lambda体} 只有一条语句的时候,可以省略 {}{;}
    • {Lambda体} 只有一条语句的时候,并且这个语句还是 return 语句的时候,return 也可以省略,但是如果 {;} 没有省略,那么 return 是不可以省略的。
    • (形参列表) 的类型可以省略。
    • (形参列表) 的形参个数只有一个,那么可以将数据类型和 () 一起省略,但是形参名不能省略。
    • (形参列表) 是空参的时候,() 不能省略。
  • 示例:

  1. package com.github.demo4;
  2. import java.util.function.IntBinaryOperator;
  3. /**
  4. * @author 许大仙
  5. * @version 1.0
  6. * @since 2021-10-09 11:00
  7. */
  8. public class Test {
  9. public static void main(String[] args) {
  10. useIntBinaryOperator(1, 2, (a, b) -> a + b);
  11. }
  12. public static void useIntBinaryOperator(int a, int b, IntBinaryOperator operator) {
  13. int result = operator.applyAsInt(a, b);
  14. System.out.println("result = " + result);
  15. }
  16. }
  • 示例:
  1. package com.github.demo5;
  2. import java.util.Set;
  3. import java.util.TreeSet;
  4. /**
  5. * @author 许大仙
  6. * @version 1.0
  7. * @since 2021-10-09 13:36
  8. */
  9. public class Test {
  10. public static void main(String[] args) {
  11. Set<Integer> set = new TreeSet<>((a, b) -> a - b);
  12. set.add(10);
  13. set.add(100);
  14. set.add(-20);
  15. set.add(5);
  16. set.add(1);
  17. System.out.println("set = " + set); // set = [-20, 1, 5, 10, 100]
  18. }
  19. }
  • 示例:
  1. package com.github.demo6;
  2. import java.util.Arrays;
  3. import java.util.List;
  4. import java.util.function.Consumer;
  5. import java.util.function.Function;
  6. import java.util.function.Predicate;
  7. import java.util.function.Supplier;
  8. /**
  9. * @author 许大仙
  10. * @version 1.0
  11. * @since 2021-10-09 13:41
  12. */
  13. public class Test {
  14. public static void main(String[] args) {
  15. userConsumer("abc", s -> System.out.println(s));
  16. useSupplier(() -> String.valueOf(Math.random()));
  17. usePredicate(Arrays.asList("a", "b", "c"), (s) -> s.equals("a"));
  18. useFunction(1, (i) -> String.valueOf(i));
  19. }
  20. public static void userConsumer(String str, Consumer<String> consumer) {
  21. consumer.accept(str);
  22. }
  23. public static void useSupplier(Supplier<String> supplier) {
  24. String s = supplier.get();
  25. System.out.println(s);
  26. }
  27. public static void usePredicate(List<String> list, Predicate<String> predicate) {
  28. for (String s : list) {
  29. if (predicate.test(s)) {
  30. System.out.println(s);
  31. }
  32. }
  33. }
  34. public static void useFunction(Integer num, Function<Integer, String> function) {
  35. String apply = function.apply(num);
  36. System.out.println(apply);
  37. }
  38. }

2.3 Lambda 表达式的练习

2.3.1 JDK 中的消费型接口

  • 在 JDK 8 中的 Collection 接口的父接口 Iterable 接口中增加了一个默认方法,该方法是消费型接口:
public interface Iterable<T> {
    // 遍历Collection集合的每个元素
    default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }   
    ... 
}
  • 在 JDK 8 中的 Map 集合中增加了一个默认方法,该方法是消费型接口:
public interface Map<K, V> {
    // 遍历Map集合的每一个entry对象
    default void forEach(BiConsumer<? super K, ? super V> action) {
        Objects.requireNonNull(action);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch (IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
            action.accept(k, v);
        }
    }
    ...
}
  • 示例:
package com.github.demo7;

import java.util.Arrays;
import java.util.List;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 13:55
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
        // 遍历List集合中的每个元素
        list.forEach(i -> System.out.println(i));
    }
}
  • 示例:
package com.github.demo8;

import java.util.HashMap;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 14:40
 */
public class Test {
    public static void main(String[] args) {
        HashMap<String, String> map = new HashMap<>();

        map.put("1", "java");
        map.put("2", "c");
        map.put("3", "python");
        map.put("4", "vb");
        map.put("5", "c#");

        // 遍历Map中的每个元素
        map.forEach((k, v) -> System.out.println(k + ":" + v));
    }
}

2.3.2 JDK 中的供给型接口

  • 在 JDK 8 中增加 Stream 接口,java.util.stream.Stream<T> 是一个数据流,该方法是供给型接口,可以创建 Stream 对象:
public interface Stream<T> extends BaseStream<T, Stream<T>> {

    public static<T> Stream<T> generate(Supplier<? extends T> s) {
    Objects.requireNonNull(s);
        return StreamSupport.stream(
            new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
    }
    ...
}
  • 示例:
package com.github.demo9;

import java.util.Random;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 14:55
 */
public class Test {
    public static void main(String[] args) {
        Stream.generate(() -> new Random().nextInt(10) + 1).limit(10).forEach(num -> System.out.println(num));
    }
}

2.3.4 JDK 中函数型接口

  • 在 JDK 8 中的 Map 集合增加了很多函数型接口:
public interface Map<K, V> {
    // 替换Map中的值
    default void replaceAll(BiFunction<? super K, ? super V, ? extends V> function) {
        Objects.requireNonNull(function);
        for (Map.Entry<K, V> entry : entrySet()) {
            K k;
            V v;
            try {
                k = entry.getKey();
                v = entry.getValue();
            } catch (IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }

            // ise thrown from function is not a cme.
            v = function.apply(k, v);

            try {
                entry.setValue(v);
            } catch (IllegalStateException ise) {
                // this usually means the entry is no longer in the map.
                throw new ConcurrentModificationException(ise);
            }
        }
    }
    ...
}
  • 示例:
package com.github.demo10;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 15:19
 */
public class Employee {

    private Integer id;

    private String name;

    private Double salary;

    public Employee() {}

    public Employee(Integer id, String name, Double salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getId() {
        return this.id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Double getSalary() {
        return this.salary;
    }

    public void setSalary(Double salary) {
        this.salary = salary;
    }

    @Override
    public String toString() {
        return "Employee{" + "name='" + this.name + '\'' + ", id=" + this.id + ", salary=" + this.salary + '}';
    }
}
package com.github.demo10;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 15:20
 */
public class Test {
    public static void main(String[] args) {
        Map<Integer, Employee> map = new HashMap<>();

        Employee e1 = new Employee(1, "张三", 5000.00);
        Employee e2 = new Employee(2, "李四", 15000.00);
        Employee e3 = new Employee(3, "王五", 75000.00);
        Employee e4 = new Employee(4, "赵六", 7000.00);
        Employee e5 = new Employee(5, "田七", 200.00);

        map.put(e1.getId(), e1);
        map.put(e2.getId(), e2);
        map.put(e3.getId(), e3);
        map.put(e4.getId(), e4);
        map.put(e5.getId(), e5);

        // 将工资小于10000元的员工的工资设置为10000
        map.replaceAll((k, v) -> {
            if (v.getSalary() < 10000) {
                v.setSalary(10000.00);
            }
            return v;
        });

        map.forEach((k, v) -> System.out.println(k + ":" + v));
    }
}
  • 示例:
package com.github.demo11;

import java.util.HashMap;
import java.util.Map;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 15:32
 */
public class Test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();

        map.put("1", "java");
        map.put("2", "c");
        map.put("3", "python");
        map.put("4", "vb");
        map.put("5", "c#");

        // 将Map中的java替换成JavaEE
        map.replaceAll((k, v) -> {
            if (v.equals("java")) {
                v = "JavaEE";
            }
            return v;
        });

        map.forEach((k, v) -> System.out.println(k + ":" + v));
    }
}

2.3.5 JDK 中的断言型接口

  • 在 JDK 8 中,Collection 接口新增了一个断言型接口:
public interface Collection<E> extends Iterable<E> {
    // 删除集合中满足指定条件的元素
    default boolean removeIf(Predicate<? super E> filter) {
        Objects.requireNonNull(filter);
        boolean removed = false;
        final Iterator<E> each = iterator();
        while (each.hasNext()) {
            if (filter.test(each.next())) {
                each.remove();
                removed = true;
            }
        }
        return removed;
    }
}
  • 示例:
package com.github.demo12;

import java.util.ArrayList;
import java.util.List;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 16:02
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();

        list.add(1);
        list.add(2);
        list.add(3);
        list.add(4);
        list.add(5);

        list.removeIf(i -> 1 == i);

        list.forEach(i -> System.out.println(i));
    }
}

2.4 方法引用和构造器引用

2.4.1 概述

  • Lambda 表达式是用来简化函数式接口的变量或形参赋值的语法,而方法引用和构造器引用是为了简化 Lambda 表达式的。
  • 当 Lambda 表达式满足如下的一些特殊情况,还可以再简化:
    • ① Lambda 体只有一条语句,并且是通过调用一个对象(或类)的方法来完成的。
      • 例如:System.out 对象,调用 println() 方法来完成 Lambda 体。Math 类,调用 random() 静态方法来完成 Lambda 体。
    • ② Lambda 表达式的形参正好是给该方法的实参。
      • 例如:t -> System.out.println(t)()->Math.random()

2.4.2 方法引用

  • 语法:
实例对象名::实例方法
类名::静态方法
类名::实例方法
  • 说明:

    • :: 是方法引用操作符。
    • Lambda 表达式的形参列表全部在 Lambda 体中使用了,要么作为调用方法的对象,要么作为调用方法的实参。
    • 在整个 Lambda 中没有额外的数据。
  • 示例:

package com.github.demo13;

import java.util.Arrays;
import java.util.List;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 16:19
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> map = Arrays.asList(1, 2, 3, 4, 5);

        map.forEach(System.out::println);
    }
}
  • 示例:
package com.github.demo14;

import java.util.Arrays;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 16:56
 */
public class Test {
    public static void main(String[] args) {
        String[] arr = {"hello", "world", "java", "c"};

        Arrays.sort(arr, String::compareToIgnoreCase);

        Arrays.asList(arr).forEach(System.out::println);
    }
}
  • 示例:
package com.github.demo15;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 17:01
 */
public class Test {
    public static void main(String[] args) {
        Stream.generate(Math::random).limit(10).forEach(System.out::println);
    }
}

2.4.3 构造器引用

  • 语法:
类名::new
数组类型::new
  • 说明:

    • 当 Lambda 表达式是为了创建一个对象,并且满足 Lambda 表达式形参,正好是创建给这个对象的构造器的实参列表。
    • 当 Lambda 表达式是为了创建一个数组对象,并且满足 Lambda 表达式形参,正好是创建这个数组对象的长度。
  • 示例:

package com.github.demo16;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 17:20
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3);
        Stream<int[]> stream1 = stream.map((i) -> new int[i]);
        // Lambda表达式
        stream1.forEach((i) -> System.out.println(i.length));

        System.out.println("---------------------");

        Stream<Integer> stream11 = Stream.of(1, 2, 3);
        Stream<int[]> stream2 = stream11.map(int[]::new);
        // 构造引用
        stream2.forEach((i) -> System.out.println(i.length));
    }
}
  • 示例:
package com.github.demo17;

import java.math.BigDecimal;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 17:30
 */
public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("1", "2", "3");

        Stream<BigDecimal> stream1 = stream.map(s -> new BigDecimal(s));

        stream1.forEach(System.out::println);

        System.out.println("---------------");

        Stream<String> stream11 = Stream.of("1", "2", "3");

        Stream<BigDecimal> stream2 = stream11.map(BigDecimal::new);

        stream2.forEach(System.out::println);

    }
}

第三章:Stream API

3.1 概述

  • JDK 8 有两个重大的改变。第一个是 Lambda 表达式,另外一个是 Stream API 。
  • Stream API (java.util.stream) 将真正的函数式编程风格引入到 Java 中。这是目前为止对 Java 类库最好的补充,因为 Stream API 可以极大的提高 Java 程序员的生产力,让程序员写出高效、干净、简洁的代码。
  • Stream 是 JDK 8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用 Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询,当然,也可以使用 Stream API 来并行执行操作。换言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
  • Stream 是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream 流讲的是计算,负责处理数据!”。

    注意:

    • ① Stream 自己不会存储元素。
    • ② Stream 不会改变源对象,每次处理读会返回一个持有结果的新的 Stream 。
    • ③ Stream 操作是延迟执行的,意味着会等到需要结果的时候才执行。

3.2 Stream 流的操作步骤

  • ① 创建 Stream :通过一个数据源(如:集合、数组),获取一个流。
  • ② 中间操作:中间操作是个操作链,对数据源的数据进行 n 次处理,但是在终结操作前,并不会真正的执行。
  • ③ 终止操作:一旦执行终止操作,就执行中间操作链,最终产生结果并结束 Stream 。

Stream流的操作步骤.png

3.3 创建 Stream

3.3.1 通过集合创建 Stream

  • JDK 8 中的 Collection 接口新增了两个获取流的方法:
  • 获取一个顺序流:
default Stream<E> stream() {}
  • 获取一个并行流:
default Stream<E> parallelStream() {}
  • 示例:
package com.github.stream.demo1;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * 通过集合创建Stream
 * 
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 20:31
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
        // 获取顺序流
        Stream<Integer> stream = list.stream();

        stream.forEach(System.out::println);
    }
}

3.3.2 通过数组创建 Stream

  • JDK 8 中的 Arrays 新增了静态方法,用来获取 Stream 流:
public static <T> Stream<T> stream(T[] array) {}
  • 重载形式,能够处理对应基本类型的数组:
  • 获取一个整型数据流:
public static IntStream stream(int[] array) {}
  • 获取一个长整型数据流:
public static LongStream stream(long[] array) {}
  • 获取一个浮点型数据流:
public static DoubleStream stream(double[] array) {}
  • 示例:
package com.github.stream.demo2;

import java.util.Arrays;
import java.util.stream.Stream;

/**
 * 通过数组创建Stream
 * 
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-09 20:35
 */
public class Test {
    public static void main(String[] args) {
        String[] arr = {"PHP", "is", "the", "best", "language", "in", "the", "world"};

        Stream<String> stream = Arrays.stream(arr);

        stream.forEach(System.out::println);
    }
}

3.3.3 通过 Stream.of() 方法创建 Stream

  • 可以调用 Stream 类静态方法 of() 方法,显示的创建一个流:
public static<T> Stream<T> of(T... values){}
public static<T> Stream<T> of(T t) {}
  • 示例:
package com.github.stream.demo3;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 06:29
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4);
        System.out.println("stream = " + stream);
    }
}

3.3.4 通过无限流创建 Stream

  • 可以使用 Stream 类的静方法 iterate 和 generate 创建无限流。
public static<T> Stream<T> generate(Supplier<? extends T> s) {}
public static<T> Stream<T> iterate(T seed, Predicate<? super T> hasNext, UnaryOperator<T> next) {}
  • 示例:
package com.github.stream.demo4;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 06:35
 */
public class Test {
    public static void main(String[] args) {

        Stream<Integer> stream = Stream.iterate(1, num -> num + 1);

        stream.limit(10).forEach(System.out::println);

    }
}
  • 示例:
package com.github.stream.demo5;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 06:40
 */
public class Test {
    public static void main(String[] args) {
        Stream<Double> stream = Stream.generate(Math::random);

        stream.limit(10).forEach(System.out::println);
    }
}

3.4 中间操作

3.4.1 概述

  • 多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不执行任何处理,而在终止操作时一次性全部处理完,称为 “惰性求值”

3.4.2 filter

  • 根据 predicate 的返回值,过滤某些元素。
Stream<T> filter(Predicate<? super T> predicate);
  • 示例:
package com.github.stream.demo6;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 06:42
 */
public class Test {
    public static void main(String[] args) {
        // 创建Stream
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);
        // 过滤,如果满足条件,就过滤出来;否则,排除掉。
        // 生活中:过滤水,当然是过滤水中的杂质,将杂质排除掉。
        Stream<Integer> integerStream = stream.filter(i -> i % 2 == 0);
        // 终结操作
        integerStream.forEach(System.out::println);
    }
}

3.4.3 distinct

  • 通过流所生成的 equals() 去除重复元素:
Stream<T> distinct();
  • 示例:
package com.github.stream.demo7;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 09:05
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 1, 2, 3, 4);

        Stream<Integer> distinct = stream.distinct();

        distinct.forEach(System.out::println);
    }
}

3.4.4 limit

  • 截断流,使其元素不超过给定数量:
Stream<T> limit(long maxSize);
  • 示例:
package com.github.stream.demo8;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 09:10
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);

        Stream<Integer> limit = stream.limit(5);

        limit.forEach(System.out::println);
    }
}

3.4.5 skip

  • 跳过前 n 个元素的流,如果流中的元素不足 n 个,则返回一个空流:
Stream<T> skip(long n);
  • 示例:
package com.github.stream.demo9;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 09:18
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 0);

        Stream<Integer> skip = stream.skip(4);

        skip.forEach(System.out::println);
    }
}

3.4.6 peek

  • peek ,对每个元素都进行 Lambda 操作,一般用于 debug 流。
Stream<T> peek(Consumer<? super T> action);
  • 示例:
package com.github.stream.demo10;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 09:35
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6, 7, 8);
        // peek不会终止流,peek属于中间操作
        Stream<Integer> peek = stream.peek(i -> System.out.println(i));

        long count = peek.count();
        System.out.println("count = " + count);
    }
}

3.4.7 sorted

  • 对流中的数据进行自然排序:
Stream<T> sorted();
  • 对流中的数据进行自定义排序:
Stream<T> sorted(Comparator<? super T> comparator);
  • 示例:
package com.github.stream.demo11;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 09:54
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(3, -1, 2, 4, 0, -90, 4, 56);

        Stream<Integer> sorted = stream.sorted();

        sorted.forEach(System.out::println);
    }
}
  • 示例:
package com.github.stream.demo12;

import java.util.Comparator;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 09:55
 */
public class Test {
    public static void main(String[] args) {
        Stream<String> stream = Stream.of("a", "b", "c", "d");

        Stream<String> sorted = stream.sorted(Comparator.reverseOrder());

        sorted.forEach(System.out::println);
    }
}

3.4.8 map

  • 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
<R> Stream<R> map(Function<? super T, ? extends R> mapper);
  • 示例:
package com.github.stream.demo13;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:06
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);

        stream = stream.map(i -> i + 1);

        stream.forEach(System.out::println);
    }
}
  • 示例:
package com.github.stream.demo14;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:10
 */
public class Person {
    private String name;

    private Integer age;

    public Person() {}

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return this.name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return this.age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" + "name='" + this.name + '\'' + ", age=" + this.age + '}';
    }
}
package com.github.stream.demo14;

import java.util.Arrays;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:10
 */
public class Test {
    public static void main(String[] args) {
        Stream<Person> stream = Arrays.asList(new Person("张三", 18), new Person("李四", 8), new Person("王五", 12)).stream();

        Stream<String> nameStream = stream.map(Person::getName);

        nameStream.forEach(System.out::println);
    }
}

3.4.9 所有中间操作方法列表

方 法 描 述
filter(Predicate p) 接收 Lambda , 从流中排除某些元素。
distinct() 筛选,通过流所生成元素的equals() 去除重复元素。
limit(long maxSize) 截断流,使其元素不超过给定数量。
skip(long n) 跳过元素,返回一个扔掉了前 n 个元素的流。若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补。
peek(Consumeraction) 接收Lambda,对流中的每个数据执行Lambda体操作。
sorted() 产生一个新流,其中按自然顺序排序。
sorted(Comparator com) 产生一个新流,其中按比较器顺序排序。
map(Function f) 接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
mapToDouble(ToDoubleFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
mapToInt(ToIntFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
mapToLong(ToLongFunction f) 接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

3.5 终止操作

3.5.1 概述

  • 终止操作会从流的流水线生成结果,其结果可以是任何不是流的值,比如:List 、Integer ,甚至可以是 void 。流进行了终止操作之后,不能再次使用。

3.5.2 forEach迭代

  • 迭代流中的元素:
void forEach(Consumer<? super T> action);
  • 示例:
package com.github.stream.demo15;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:20
 */
public class Test {
    public static void main(String[] args) {
        Stream.of(1, 2, 3, 4, 5, 6).forEach(System.out::println);
    }
}

3.5.3 count

  • 返回流所操作的数据的总数:
long count();
  • 示例:
package com.github.stream.demo16;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:23
 */
public class Test {
    public static void main(String[] args) {
        long count = Stream.of(1, 2, 3, 4, 5, 6, 6, 5, 4, 3, 2, 1).distinct().count();
        System.out.println("count = " + count); // count = 6
    }
}

3.5.4 allMatch

  • 检查流所操作的所有元素是否匹配规则:
boolean allMatch(Predicate<? super T> predicate);
  • 示例:
package com.github.stream.demo17;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:25
 */
public class Test {
    public static void main(String[] args) {
        // 判断流所操作的元素是否都是偶数
        boolean allMatch = Stream.of(1, 2, 3, 4, 5, 6).allMatch(i -> i % 2 == 0);
        System.out.println("allMatch = " + allMatch); // allMatch = false
    }
}

3.5.5 anyMatch

  • 判断流所操作的元素是否有一个元素匹配规则:
boolean anyMatch(Predicate<? super T> predicate);
  • 示例:
package com.github.stream.demo18;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:28
 */
public class Test {
    public static void main(String[] args) {
        // 判断流所操作的元素是否有一个是偶数
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5, 6);
        boolean anyMatch = stream.anyMatch(i -> i % 2 == 0);
        System.out.println("anyMatch = " + anyMatch); // anyMatch = true
    }
}

3.5.6 FindFirst

  • 返回流所操作元素中的第一个元素:
Optional<T> findFirst();
  • 示例:
package com.github.stream.demo19;

import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:33
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> distinct = Stream.of(2, 3, 4, 5, 5, 4, 3, 2, 1).distinct();

        Optional<Integer> first = distinct.findFirst();

        Integer integer = first.get();

        System.out.println("integer = " + integer); // integer = 2
    }
}

3.5.7 max

  • 返回流所操作元素中的最大值:
Optional<T> max(Comparator<? super T> comparator);
  • 示例:
package com.github.stream.demo20;

import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:36
 */
public class Test {
    public static void main(String[] args) {
        Optional<Integer> optional = Stream.of(1, 2, -1, 10, 2).distinct().max(Integer::compareTo);

        Integer integer = optional.get();

        System.out.println("integer = " + integer); // integer = 10
    }
}

3.5.8 reduce

  • 可以将流中元素反复结合起来,得到一个值。返回 T :
T reduce(T identity, BinaryOperator<T> accumulator);
  • 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
Optional<T> reduce(BinaryOperator<T> accumulator);
  • 示例:
package com.github.stream.demo22;

import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:41
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);

        Integer reduce = stream.reduce(0, Integer::sum);

        System.out.println("reduce = " + reduce); // reduce = 15
    }
}
  • 示例:
package com.github.stream.demo23;

import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:42
 */
public class Test {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4);

        Optional<Integer> optional = stream.reduce(Integer::sum);

        Integer reduce = optional.get();

        System.out.println("reduce = " + reduce); // reduce = 10
    }
}
  • 示例:
package com.github.stream.demo24;

import java.util.Optional;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:49
 */
public class Test {
    public static void main(String[] args) {
        // 求最大值
        Optional<Integer> optional = Stream.of(-1, -10, 3, 5, 9).reduce(Integer::max);

        Integer reduce = optional.get();

        System.out.println("reduce = " + reduce); // reduce = 9
    }
}

3.5.9 collect

  • 收集流所操作的元素并将其转换为其他形式:
<R, A> R collect(Collector<? super T, A, R> collector);
  • 示例:
package com.github.stream.demo21;

import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 10:37
 */
public class Test {
    public static void main(String[] args) {
        List<Integer> collect = Stream.of(1, 1, 2, 1, 3, 4).distinct().collect(Collectors.toList());
        System.out.println("collect = " + collect); // collect = [1, 2, 3, 4]
    }
}

3.5.10 所有终止操作的方法列表

方法 描述
booleanallMatch(Predicate p) 检查是否匹配所有元素
booleananyMatch(Predicate p) 检查是否至少匹配一个元素
booleannoneMatch(Predicate p) 检查是否没有匹配所有元素
Optional<T>
findFirst()
返回第一个元素
Optional<T>
findAny()
返回当前流中的任意元素
longcount() 返回流中元素总数
Optional<T>
max(Comparator c)
返回流中最大值
Optional<T>
min(Comparator c)
返回流中最小值
voidforEach(Consumer c) 迭代
Treduce(T iden, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
Ureduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
Rcollect(Collector c) 将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
  • Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List 、Set 、Map )。
  • 另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。

第四章:Optional 类

4.1 概述

  • 到目前为止,臭名昭著的空指针异常是导致 Java 应用程序失败的最常见的原因。以前,为了解决空指针异常,Google 公司著名的 Guava 项目引入了 Optional 类,Guava 通过使用检查 null 值的方式来防止代码污染,它鼓励程序员写干净的代码。受到 Google 的 Guava 的启发,Optional 类已经成为了 JDK 8 类库的一部分。
  • Optional 实际上是个容器:它可以保存类型 T 的值,或者仅仅保存 null 。Optional 提供了很多有用的方法,这样我们就不用显示进行 null 值检测。

4.2 创建 Optional 对象

  • 创建一个空的(没有元素的)Optional :
public static<T> Optional<T> empty() {}
  • 创建一个非空的(有元素的)Optional :
public static <T> Optional<T> of(T value) {}
  • 创建一个可能为空的(没有元素的),也可能是非空的(有元素的)Optional :
public static <T> Optional<T> ofNullable(T value) {}
  • 示例:
package com.github.optional.demo;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 12:37
 */
public class Test {
    public static void main(String[] args) {
        // 创建空的Optional
        Optional<String> empty = Optional.empty();
        System.out.println("empty = " + empty);
    }
}
  • 示例:
package com.github.optional.demo1;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 12:39
 */
public class Test {
    public static void main(String[] args) {
        // 创建一个非空的Optional
        Optional<String> optional = Optional.of("abc");
        System.out.println("optional = " + optional);
    }
}
  • 示例:
package com.github.optional.demo2;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 12:41
 */
public class Test {
    public static void main(String[] args) {
        // 创建一个可能为空的,也可能是非空的Optional
        Optional<String> optional = Optional.ofNullable("abc");
        System.out.println("optional = " + optional); // optional = Optional[abc]

        optional = Optional.ofNullable(null);
        System.out.println("optional = " + optional); // optional = Optional.empty
    }
}

4.3 从 Optional 容器中获取元素

  • 获取元素,要求Optional容器必须非空
public T get() {}
  • 如果容器中有元素,就返回容器中的元素;否则,就返回默认值:
public T orElse(T other) {}
  • 如果容器中有元素,就返回容器中的元素;否则,就返回 Supplier 接口的 Lambda 表达式提供的值:
public T orElseGet(Supplier<? extends T> other) {}
  • 如果容器中有元素,就返回容器中的元素;否则,就抛出指定的异常:
public <X extends Throwable> T orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {}
  • 示例:
package com.github.optional.demo3;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 12:50
 */
public class Test {
    public static void main(String[] args) {
        Optional<String> optional = Optional.ofNullable("abc");

        String s = optional.get();

        System.out.println("s = " + s);
    }
}
  • 示例:
package com.github.optional.demo4;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 12:53
 */
public class Test {
    public static void main(String[] args) {
        String str = "abc";

        // Optional容器中有元素
        Optional<String> optional = Optional.ofNullable(str);
        String s = optional.orElse("bcd");
        System.out.println("s = " + s); // s = abc

        str = null;
        // Optional容器中没有元素
        Optional<String> optional2 = Optional.ofNullable(str);
        s = optional2.orElse("bcd");
        System.out.println("s = " + s); // s = bcd
    }
}
  • 示例:
package com.github.optional.demo5;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 12:59
 */
public class Test {
    public static void main(String[] args) {
        String str = "abc";
        // Optional容器有元素
        Optional<String> optional = Optional.ofNullable(str);
        String s = optional.orElseGet(String::new);
        System.out.println("s = " + s); // s = abc

        // Optional容器没有元素
        str = null;
        Optional<String> optional2 = Optional.ofNullable(str);
        s = optional2.orElseGet(String::new);
        System.out.println("s  = " + s); // s =
    }
}
  • 示例:
package com.github.optional.demo6;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 13:03
 */
public class Test {
    public static void main(String[] args) {
        String str = "abc";

        // Optional容器中有元素
        Optional<String> optional = Optional.ofNullable(str);
        String s = optional.orElseThrow(() -> new RuntimeException("str不能为null"));
        System.out.println("s = " + s); // s = abc

        // Optional容器中没有元素
        str = null;
        Optional<String> optional2 = Optional.ofNullable(str);
        s = optional2.orElseThrow(() -> new RuntimeException("str不能为null"));
        System.out.println("s = " + s); // Exception in thread "main" java.lang.RuntimeException: str不能为null
    }
}

4.3 Optional 的判断方法

  • 判断 Optional 容器中是否有元素:
public boolean isPresent() {}
  • 判断 Optional 容器中是否有元素,如果有,则对它进行 Consumer 指定的操作;否则,啥也不做。
public void ifPresent(Consumer<? super T> consumer) {}
  • 判断 Optional 容器中是否有元素,如果有,则对它进行 Function 接口指定的操作;否则,啥也不做。
public<U> Optional<U> map(Function<? super T, ? extends U> mapper) {}
  • 示例:
package com.github.optional.demo7;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 13:13
 */
public class Test {
    public static void main(String[] args) {
        String str = "abc";
        Optional<String> optional = Optional.ofNullable(str);
        boolean present = optional.isPresent();
        System.out.println("present = " + present); // present = true

        str = null;
        Optional<String> optional2 = Optional.ofNullable(str);
        boolean present2 = optional2.isPresent();
        System.out.println("present2 = " + present2); // present2 = false

    }
}
  • 示例:
package com.github.optional.demo8;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 13:13
 */
public class Test {
    public static void main(String[] args) {
        String str = "abc";
        Optional<String> optional = Optional.ofNullable(str);
        optional.ifPresent(System.out::println); // abc

        str = null;
        Optional<String> optional2 = Optional.ofNullable(str);
        optional2.ifPresent(System.out::println);

    }
}
  • 示例:
package com.github.optional.demo9;

import java.util.Optional;

/**
 * @author 许大仙
 * @version 1.0
 * @since 2021-10-10 13:13
 */
public class Test {
    public static void main(String[] args) {
        String str = "abc";
        Optional<String> optional = Optional.ofNullable(str);
        optional = optional.map(String::toUpperCase);
        System.out.println("optional = " + optional); // optional = Optional[ABC]

        str = null;
        Optional<String> optional2 = Optional.ofNullable(str);
        optional2 = optional2.map(String::toUpperCase);
        System.out.println("optional2 = " + optional2); // optional2 = Optional.empty
    }
}