Lambda表达式与StreamAPI
Lambda表达式与StreamAPI
Author: Msuenb
Date: 2023-02022
函数式编程思想
面向对象过分强调"必须通过对象的形式来做事情",而函数式思想则是尽量忽略面向对象的复杂语法——强调做什么,而不是谁来做。
JDK8中引入了Lambda表达式,Java也开始支持函数式编程。
public class TestLambda {
    @Test
    public void test1(){
        String[] arr = {"hello", "java", "good", "world", "ok"};
        // 把上面的字符串按照长短排序,从短到长
        Arrays.sort(arr, new Comparator<String>() {
            @Override
            public int compare(String o1, String o2) {
                // return o1.length()-o2.length();
                return Integer.compare(o1.length(), o2.length());
            }
        });
        /*
        上面的写法使用了匿名内部类,既声明了一个类,又创建了一个对象。
        创建这个对象的目的是为了给sort方法的第二个形参c赋值。
        声明匿名内部类的目的是为了重写public int compare(String o1, String o2)
         */
        System.out.println(Arrays.toString(arr));
    }
    @Test
    public void test2(){
        String[] arr = {"hello", "java", "good", "world", "ok"};
        //这个需求中我们关心的是什么? 如何比较两个字符串的大小,至于对象不重要
        Arrays.sort(arr, (String o1, String o2) -> { return Integer.compare(o1.length(), o2.length()); });
        System.out.println(Arrays.toString(arr));
    }
}
这里并不希望创建一个匿名内部类对象,只是为了做这件事情而不得不创建一个对象。真正希望做的事情是:将compareTo方法体内的代码传递给sort方法知晓。
传递一段代码——这才是真正的目的。而创建对象只是受限于面向对象语法而不得不采取的一种手段方式。使用Lambda表达式不再有“不得不创建接口对象”的束缚,就是这么简单!
Lambda表达式
Lambda表达式是用来给"函数式接口"的变量或形参赋值用的。其实本质上,Lambda表达式是用于实现"函数式接口"的抽象方法的语法格式,或是给函数式接口的变量传递一段实现抽象方法的代码。
Lambda表达式语法格式:
(形参列表) -> {Lambda体}
语法格式说明:
- (形参列表):它就是你要赋值的函数式接口的抽象方法的(形参列表)
- {Lambda体}:就是实现这个抽象方法的方法体
- ->:称为Lambda操作符(减号和大于号中间不能有空格)
使用演示:
@Test
public void test1(){
    Runnable r = new Runnable() {
        @Override
        public void run() {
            System.out.println("Hello, World!");
        }
    };
}
@Test
public void test2(){
    Runnable r = () -> {
        System.out.println("Hello, World"); 
    };
}
Lambda 表达式的简化:
- 当{Lambda体}中只有一句话时,可以省略{};
- 当{Lambda体}中只有一句语句时,并且这个语句还是一个return语句,那么{};和return可以省略。
- 当Lambda表达式(形参列表)的类型已知,获取根据泛型规则可以自动推断,那么(形参列表)的数据类型可以省略。
- 当Lambda表达式(形参列表)的形参个数只有一个,并且类型已知或可以自动推断,可以只写形参名。
- 当Lambda表达式(形参列表)是空参时,() 不能省略
@Test
public void test2(){
    Runnable r = () -> System.out.println("Hello, World"); 
}
@Test
public void test6(){
    String[] arr = {"hello", "java", "good", "world", "ok"};
    //把上面的字符串按照长短排序,从短到长
    Arrays.sort(arr, (o1, o2) -> Integer.compare(o1.length(), o2.length()));
    System.out.println(Arrays.toString(arr));
}
函数式接口
Java8在java.util.function新增了很多函数式接口:主要分为四大类,消费型、供给型、判断型、功能型。基本可以满足平时的开发需求,当然也可以自定义函数式接口。
- 
消费型接口 消费型接口的抽象方法特点:有形参,但是返回值类型是void 序号 接口名 抽象方法 描述 1 Consumer void accept(T t) 接收一个对象用于完成功能 2 BiConsumer<T,U> void accept(T t, U u) 接收两个对象用于完成功能 3 DoubleConsumer void accept(double value) 接收一个double值 4 IntConsumer void accept(int value) 接收一个int值 5 LongConsumer void accept(long value) 接收一个long值 6 ObjDoubleConsumer void accept(T t, double value) 接收一个对象和一个double值 7 ObjIntConsumer void accept(T t, int value) 接收一个对象和一个int值 8 ObjLongConsumer void accept(T t, long value) 接收一个对象和一个long值 在JDK1.8中,java.lang.Iterable接口中增加了一个默认方法: - public default void forEach(Consumer<? super T> action)该方法功能是遍历Collection集合,并将传递给action参数的操作代码应用在每一个元素上。
 @Test public void consumerTest() { List<String> list = Arrays.asList("java","c","python","c++","VB","C#"); list.forEach(s -> System.out.println(s)); }
- 
供给型接口 供给型接口的抽象方法特点:无参,但是有返回值 序号 接口名 抽象方法 描述 1 Supplier T get() 返回一个对象 2 BooleanSupplier boolean getAsBoolean() 返回一个boolean值 3 DoubleSupplier double getAsDouble() 返回一个double值 4 IntSupplier int getAsInt() 返回一个int值 5 LongSupplier long getAsLong() 返回一个long值 @Test public void supplierTest() { Supplier<String> supplier = () -> "hello, world"; System.out.println(supplier.get()); }
- 
判断型接口 判断型接口的抽象方法特点:有参,但是返回值类型是boolean结果。 序号 接口名 抽象方法 描述 1 Predicate boolean test(T t) 接收一个对象 2 BiPredicate<T,U> boolean test(T t, U u) 接收两个对象 3 DoublePredicate boolean test(double value) 接收一个double值 4 IntPredicate boolean test(int value) 接收一个int值 5 LongPredicate boolean test(long value) 接收一个long值 JDK1.8中,Collecton接口增加了一下方法: - public default boolean removeIf(Predicate<? super E> filter)用于删除集合中满足filter指定的条件判断的。
 @Test public void removeIfTest() { ArrayList<String> strings = new ArrayList<>(); strings.add("hello"); strings.add("java"); strings.add("world"); strings.add("ok"); strings.add("yes"); System.out.println("删除之前:"); strings.forEach(s -> System.out.print(s + ", ")); // 要求删除包含o字母的元素 strings.removeIf(s -> s.contains("o")); System.out.println("\n删除之后:"); strings.forEach(s -> System.out.print(s + ", ")); }
- 
功能型接口 功能型接口的抽象方法特点:既有参数又有返回值 序号 接口名 抽象方法 描述 1 Function<T,R> R apply(T t) 接收一个T类型对象,返回一个R类型对象结果 2 UnaryOperator T apply(T t) 接收一个T类型对象,返回一个T类型对象结果 3 DoubleFunction R apply(double value) 接收一个double值,返回一个R类型对象 4 IntFunction R apply(int value) 接收一个int值,返回一个R类型对象 5 LongFunction R apply(long value) 接收一个long值,返回一个R类型对象 6 ToDoubleFunction double applyAsDouble(T value) 接收一个T类型对象,返回一个double 7 ToIntFunction int applyAsInt(T value) 接收一个T类型对象,返回一个int ... ... JDK8中,java.util.List接口新增了一个方法: - default void replaceAll(UnaryOperator operator)将该列表的每个元素替换为将该运算符应用于该元素的结果。
 @Test public void funTest() { ArrayList<String> list = new ArrayList<>(); list.add("hello"); list.add("java"); list.add("world"); // 使用Lambda表达式实现Function<T,R>接口的子接口UnaryOperator<T> // 可以实现将一个字符串首字母转为大写的功能。 list.replaceAll(s -> s.toUpperCase()); list.forEach(s -> System.out.print(s + ", ")); }
自定义函数式接口
只要确保接口中有且仅有一个抽象方法即可
例如:声明一个转换器 Convertor<T,R>,包含抽象方法change,可以将参数转换为另一个值,并返回结果。其中T是参数类型,R是返回值类型。
interface Convertor<T, R> {
    R change(T t);
}
public class CustomFunItfTest {
    public static void main(String[] args) {
        Convertor<String, Character> convertor = s -> s.charAt(0);
        Character charHello = convertor.change("hello");
        System.out.println(charHello);
    }
}
StreamAPI
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API。
Stream特点
Stream API ( java.util.stream) 把真正的函数式编程风格引入到Java中。这是目前为止对Java类库最好的补充,因为Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。
Stream 是 Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的操作,就类似于使用SQL执行的数据库查询。也可以使用Stream API来并行执行操作。简言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
Stream是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。“集合讲的是数据,负责存储数据,Stream流讲的是计算,负责处理数据!”
- Stream 自己不会存储元素
- Stream 不会改变源对象。每次处理都会返回一个持有结果的新Stream
- Stream 操作是延迟执行的。这意味着它们会等到需要结果时才执行
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
public class TestStream {
    public static void main(String[] args) {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "hello", "java", "world", "yes", "no");
        // 1.创建Stream
        Stream<String> stream = list.stream();
        // 2.加工处理
        stream = stream.filter(s -> s.contains("o"));
        stream = stream.peek(s -> System.out.print(s + ", "));
        // 3.结束处理
        long count = stream.count();    // 没有这一步 前面不会执行
        System.out.println(count);
        System.out.println(list);   // 不会修改数据
    }
}
Stream 操作步骤
- 创建Stream:通过一个数据源(集合、数组),获取一个流
- 中间操作:每次处理都会返回一个持有结果的新Stream,即中间操作的方法仍返回Stream类型的对象,因此中间操作可以是个操作链,但在终结操作前,并不会真正执行。
- 终止操作:终止操作的返回值类型就不再是Stream了,一旦执行终止操作,就执行中间操作链,产生结果并结束Stream。
创建StreamAPI
- 
创建 Stream 方式一:通过集合 Java8 中的 Collection 接口被扩展,提供了两个获取流的方法: - 
public default Stream stream() : 返回一个顺序流 
- 
public default Stream parallelStream() : 返回一个并行流 
 
- 
- 
创建 Stream 方式二:通过数组 Java8 中的 Arrays 的静态方法 stream() 可以获取数组流: - 
public static Stream stream(T[] array): 返回一个流。重载形式,能够处理对应基本类型的数组: 
- 
public static IntStream stream(int[] array):返回一个整型数据流 
- 
public static LongStream stream(long[] array):返回一个长整型数据流 
- 
public static DoubleStream stream(double[] array):返回一个浮点型数据流 
 
- 
- 
创建 Stream 方式三:通过Stream的 of() 可以调用Stream类静态方法 of(), 通过显示值创建一个流。它可以接收任意数量的参数。 - public static Stream of(T... values) : 返回一个顺序流
 
- 
创建 Stream方式四:创建无限流 可以使用静态方法 Stream.iterate() 和 Stream.generate(), 创建无限流。 - 
public static Stream iterate(final T seed, final UnaryOperator f):返回一个无限流 
- 
public static Stream generate(Supplier s) :返回一个无限流 
 
- 
import org.junit.jupiter.api.Test;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.stream.Stream;
public class TestCreateStream {
    @Test
    public void test01() {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list, "hello", "java", "world", "yes", "no");
        Stream<String> stream = list.stream();
    }
    @Test
    public void test02() {
        String[] str = {"hello", "java", "world", "ok", "yes"};
        Arrays.stream(str);
    }
    @Test
    public void test03() {
        Stream<String> stream = Stream.of("hello", "java", "world", "ok", "yes");
    }
    @Test
    public void test04() {
        // 创建无线流
        Stream<Double> stream = Stream.generate(() -> Math.random());
        // 结束 stream
        stream.forEach(d -> System.out.println(d));
    }
    @Test
    public void test05() {
        Stream stream = Stream.iterate(1, t -> t + 1);
        // 结束stream
        stream.forEach(t -> System.out.println(t));
    }
}
中间操作API
多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何的处理!而在终止操作时一次性全部处理,称为“惰性求值”。
| 序号 | 方 法 | 描 述 | 
|---|---|---|
| 1 | Stream filter(Predicate p) | 接收 Lambda , 从流中排除某些元素 | 
| 2 | Stream distinct() | 筛选,通过流所生成元素的equals() 去除重复元素 | 
| 3 | Stream limit(long maxSize) | 截断流,使其元素不超过给定数量 | 
| 4 | Stream skip(long n) | 跳过元素,返回一个扔掉了前 n 个元素的流。 若流中元素不足 n 个,则返回一个空流。与 limit(n) 互补 | 
| 5 | Stream peek(Consumer action) | 接收Lambda,对流中的每个数据执行Lambda体操作 | 
| 6 | Stream sorted() | 产生一个新流,其中按自然顺序排序 | 
| 7 | Stream sorted(Comparator com) | 产生一个新流,其中按比较器顺序排序 | 
| 8 | Stream map(Function f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 并将其映射成一个新的元素。 | 
| 9 | Stream mapToDouble(ToDoubleFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 产生一个新的 DoubleStream。 | 
| 10 | Stream mapToInt(ToIntFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 产生一个新的 IntStream。 | 
| 11 | Stream mapToLong(ToLongFunction f) | 接收一个函数作为参数,该函数会被应用到每个元素上, 产生一个新的 LongStream。 | 
| 12 | Stream flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流, 然后把所有流连接成一个流 | 
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestMiddle {
    @Test
    public void test01() {
        Stream<Integer> stream = Stream.of(1, 2, 3, 4, 5);  // 获取stream 必须要
        // 没有中间处理操作
        stream.forEach(t -> System.out.println(t)); // 终结操作必须要
    }
    @Test
    public void test02() {
        // 操作链
        Stream.of(5, 2, 1, 3, 6, 4, 7, 9, 0)
                .filter(t -> t % 2 == 0)
                .sorted((t1, t2) -> t1 - t2)
                .forEach(t -> System.out.println(t));
    }
    @Test
    public void test03() {
        // 把一组数字中,不同的数字筛选出来,打印这些数字,并且把这些不同的数字从小到大收集到List中
        List<Integer> list = Stream.of(9, 6, 3, 2, 1, 5, 8, 7, 4, 1, 2, 5, 9, 4, 0)
                .distinct() // 去重
                .sorted()   // 排序
                .peek(t -> System.out.print(t + " "))
                .collect(Collectors.toList());
    }
    @Test
    public void test04() {
        // 随机产生一些[0,100)的整数,取出前10个
        Random random = new Random();
        List<Integer> list = Stream.generate(() -> random.nextInt(100))
                .limit(10)
                .collect(Collectors.toList());
        System.out.println(list);
    }
    @Test
    public void test05() {
        // 取出第三个元素
        Stream.of("hello", "java", "world", "ok", "yes")
                .skip(2)
                .limit(1)
                .forEach(t -> System.out.println(t));
    }
}
终结操作API
终结操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void。流进行了终止操作后,不能再次使用。
| 序号 | 返回值类型 | 方法 | 描述 | 
|---|---|---|---|
| 1 | boolean | allMatch(Predicate p) | 检查是否匹配所有元素 | 
| 2 | boolean | anyMatch(Predicate p) | 检查是否至少匹配一个元素 | 
| 3 | boolean | noneMatch(Predicate p) | 检查是否没有匹配所有元素 | 
| 4 | Optional | findFirst() | 返回第一个元素 | 
| 5 | Optional | findAny() | 返回当前流中的任意元素 | 
| 6 | long | count() | 返回流中元素总数 | 
| 7 | Optional | max(Comparator c) | 返回流中最大值 | 
| 8 | Optional | min(Comparator c) | 返回流中最小值 | 
| 9 | void | forEach(Consumer c) | 迭代 | 
| 10 | T | reduce(T iden, BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 T | 
| 11 | U | reduce(BinaryOperator b) | 可以将流中元素反复结合起来,得到一个值。返回 Optional | 
| 12 | R | collect(Collector c) | 将流转换为其他形式。接收一个 Collector接口的实现 用于给Stream中元素做汇总的方法 | 
Collector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。另外, Collectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。
import org.junit.jupiter.api.Test;
import java.util.List;
import java.util.Optional;
import java.util.function.BinaryOperator;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestEndStream {
    @Test
    public void test01() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 判断是否都满足 偶数的要求
        boolean result = stream.allMatch(t -> t % 2 == 0);
        System.out.println(result);
    }
    @Test
    public void test02() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 判断是否有数字满足 偶数的要求
        boolean result = stream.anyMatch(t -> t % 2 == 0);
        System.out.println(result);
    }
    @Test
    public void test03() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 判断是否都不满足 偶数的要求
        boolean result = stream.noneMatch(t -> t % 2 == 0);
        System.out.println(result);
    }
    @Test
    public void test04() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 筛选出所有的偶数
        stream = stream.filter(t -> t % 2 == 0);
        // 获取流中的第一个元素
        Optional<Integer> first = stream.findFirst();
        System.out.println(first);
    }
    @Test
    public void test05() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 统计流中元素的个数
        long count = stream.count();
        System.out.println(count);
    }
    @Test
    public void test06() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 找出流中的最大值
        Optional<Integer> max = stream.max((t1, t2) -> Integer.compare(t1, t2));
        System.out.println(max);
    }
    @Test
    public void test07() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 遍历流中的数据
        stream.forEach(t -> System.out.println(t));
    }
    @Test
    public void test08() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 使用reduce方法将流中的元素累加
        Optional<Integer> sum = stream.reduce((i1, i2) -> i1 + i2);
        System.out.println(sum);
    }
    @Test
    public void test09() {
        Stream<Integer> stream = Stream.of(11,  93, 50, 72, 9);
        // 筛选出所有偶数 放入集合中
        List<Integer> list = stream.filter(t -> t % 2 == 0)
                .collect(Collectors.toList());
        System.out.println(list);
    }
}
方法引用与构造器引用
Lambda表达式是可以简化函数式接口的变量与形参赋值的语法。而方法引用和构造器引用是为了简化Lambda表达式的。
当Lambda表达式满足一些特殊的情况时,还可以再简化:
- 
Lambda体只有一句语句,并且是通过调用一个对象/类的方法来完成的 
- 
Lambda表达式的形参正好全部用上,Lambda体中没有额外的数据参与 
| 序号 | 语法格式 | 场景 | 
|---|---|---|
| 1 | 实例对象名::实例方法 | Lambda表达式有多个形参,Lambda体是调用Lambda体外的某个实例对象的实例方法完成,并且Lambda表达式的形参正好依次按顺序作为该方法调用的实参 | 
| 2 | 类名::静态方法 | Lambda表达式有多个形参,Lambda体是调用某个类的静态方法完成,并且Lambda表达式的形参正好依次按顺序作为该方法调用的实参 | 
| 3 | 类名::实例方法 | Lambda表达式只有1个形参,该参数正好是Lambda体中调用方法的对象 | 
| Lambda表达式有多个形参,其中第1个参数正好是Lambda体中调用方法的对象,其余形参正好依次按顺序作为该方法调用的实参 | ||
| 4 | 类名::new | 当Lambda表达式是一个new表达式,并且Lambda表达式形参正好依次按顺序作为所调用构造器的实参 | 
| 5 | 数组类型名::new | 当Lambda表达式是一个创建数组对象的new表达式,Lambda表达式的形参正好是创建数组的长度 | 
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class TestMethodReference {
    @Test
    public void test01() {
        List<Integer> list = Arrays.asList(1, 3, 4, 8, 9);
        // list.forEach(t -> System.out.println(t));
        // out 是PrintStream对象 符合情况1  用方法引用简化
        list.forEach(System.out::println);
    }
    @Test
    public void test02() {
        // Stream<Double> stream = Stream.generate(() -> Math.random());
        // Math的静态方法 符合情况2 引用简化
        Stream<Double> stream = Stream.generate(Math::random)
                .limit(10);
        stream.forEach(System.out::println);
    }
    @Test
    public void test03() {
        String[] arr = {"hello", "java", "world", "ok", "yes"};
        // Arrays.sort(arr, ((o1, o2) -> o1.length() - o2.length()));
        // Arrays.sort(arr, Comparator.comparing(s -> s.length()));
        // 符合情况3(1)
        Arrays.sort(arr, Comparator.comparing(String::length));
        // Arrays.sort(arr, (s1, s2) -> s1.compareTo(s2));
        // 符合情况3(2)
        Arrays.sort(arr, String::compareTo);
    }
    @Test
    public void test04() {
        ArrayList<String> list = new ArrayList<>();
        Collections.addAll(list,"3.14","9.8324","0.23427123");
        // 使用数组中的元素创建BigDecimal对象
        /*List<BigDecimal> result = list.stream()
                .map(num -> new BigDecimal(num))
                .collect(Collectors.toList());*/
        List<BigDecimal> result = list.stream()
                .map(BigDecimal::new)
                .collect(Collectors.toList());
        result.forEach(System.out::println);
    }
    @Test
    public void test05() {
        Optional<Integer> opt1 = Optional.ofNullable(16);
        // Optional<String[]> opt2 = opt1.map(len -> new String[len]);
       
        Optional<String[]> opt2 = opt1.map(String[]::new);
        System.out.println(opt2.orElse(new String[0]).length);
    }
}
Optional类
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样就不用显式进行空值检测。
| 序号 | 构造器或方法 | 描述 | 
|---|---|---|
| 1 | static Optional empty() | 用来创建一个空的Optional | 
| 2 | static Optional of(T value) | 用来创建一个非空的Optional | 
| 3 | static Optional ofNullable(T value) | 用来创建一个可能是空,也可能非空的Optional | 
| 4 | T get() | 返回Optional容器中的对象。要求Optional容器必须非空。T get()与of(T value)使用是安全的 | 
| 5 | T orElse(T other) | 如果Optional容器中非空,就返回所包装值,如果为空,就用orElse(T other)other指定的默认值(备胎)代替。一般orElse(T other) 与ofNullable(T value)配合使用 | 
| 6 | T orElseGet(Supplier<? extends T> other) | 如果Optional容器中非空,就返回所包装值,如果为空,就用Supplier接口的Lambda表达式提供的值代替 | 
| 7 | T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException | 
| 8 | boolean isPresent() | 判断Optional容器中的值是否存在 | 
| 9 | void ifPresent(Consumer<? super T> consumer) | 判断Optional容器中的值是否存在,如果存在,就对它进行Consumer指定的操作,如果不存在就不做 | 
| 10 | Optional map(Function<? super T,? extends U> mapper) | 判断Optional容器中的值是否存在,如果存在,就对它进行Function接口指定的操作,如果不存在就不做 | 
import java.util.Optional;
import org.junit.Test;
public class TestOptional {
    @Test
    public void test01(){
        String str = "Hello";
        Optional<String> opt = Optional.ofNullable(str);
        //判断是否是纯字母单词,如果是,转为大写,否则保持不变
        String result = opt.filter(s->s.matches("[a-zA-Z]+"))
                .map(s -> s.toLowerCase()).orElse(str);
        System.out.println(result);
    }
    @Test
    public void test02(){
        String str = null;
        Optional<String> opt = Optional.ofNullable(str);
        // 如果是null就抛出异常
        String string = opt.orElseThrow(()->new RuntimeException("值不存在"));
        System.out.println(string);
    }
    @Test
    public void test03(){
        String str = null;
        // 如果是空就用Supplier提供的值代替
        Optional<String> opt = Optional.ofNullable(str);
        String string = opt.orElseGet(()->new String("atguigu"));
        System.out.println(string);
    }
    @Test
    public void test04(){
        String str = null;
        Optional<String> opt = Optional.ofNullable(str);
		// System.out.println(opt.get());	//java.util.NoSuchElementException: No value present
        String string = opt.orElse("hello");
        System.out.println(string);
    }
    @Test
    public void test05(){
        String str = "hello";
        Optional<String> opt = Optional.of(str); // 创建一个非空Optional对象
        String string = opt.get();
        System.out.println(string);
    }
    @Test
    public void test06(){
        String str = null;
        Optional<String> opt = Optional.ofNullable(str);
        System.out.println(opt);
    }
    @Test
    public void test07(){
        Optional<String> opt = Optional.empty(); // 创建一个空的Optional对象
        System.out.println(opt);
    }
}

 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号