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 操作步骤

  1. 创建Stream:通过一个数据源(集合、数组),获取一个流
  2. 中间操作:每次处理都会返回一个持有结果的新Stream,即中间操作的方法仍返回Stream类型的对象,因此中间操作可以是个操作链,但在终结操作前,并不会真正执行。
  3. 终止操作:终止操作的返回值类型就不再是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);
    }
}
posted @ 2023-02-24 13:02  msuenb  阅读(26)  评论(0编辑  收藏  举报