java8新特性——Function&Stream&Optional

java8新特性------Function&Stream&Optional

文章目录

函数式编程

简介

函数式编程,或称函数程序设计、泛函编程,是一种编程范型,它将电脑运算视为函数运算,并且避免使用程式状态以及可变物件。这意味着一个函数,既可以作为其它函数的输入参数值,也可以从函数中返回值,被修改或者被分配给一个变量。

比起指令式编程,函数式编程更加强调程序执行的结果而非执行的过程,倡导利用若干简单的执行单元让计算结果不断渐进,逐层推导复杂的运算,而不是设计一个复杂的执行过程。

在java中,函数式编程有以下特征:

  • 接口中有且只有一个抽象方法;
  • 接口被@FunctionalInterface注解修饰;
  • 使用Lambda表达式作为入参和返回值;

常用的函数式接口

  1. BinaryOperator extends BiFunction<T,T,T>

    表示两个相同类型的操作数进行运算,产生与操作数相同类型的结果。

    java 复制代码
    BinaryOperator<Integer> addition = (x, y) -> x + y;
    BinaryOperator<Integer> subtraction = (x, y) -> x - y;
    BinaryOperator<Integer> multiplication = (x, y) -> x * y;
    BinaryOperator<Double> division = (x, y) -> x / y;
    
    System.out.println("10 + 5 = " + addition.apply(10, 5));
    System.out.println("10 - 5 = " + subtraction.apply(10, 5));
    System.out.println("10 * 5 = " + multiplication.apply(10, 5));
    System.out.println("10.0 / 2.0 = " + division.apply(10.0, 2.0));
    
    // 执行结果
    10 + 5 = 15
    10 - 5 = 5
    10 * 5 = 50
    10.0 / 2.0 = 5.0
    java 复制代码
    List<User> list0 = Arrays.asList(
            User.builder().name("Emma Watson").age(18).sex("女").build(),
            User.builder().name("Angelina Jolie").age(19).sex("女").build(),
            User.builder().name("Emilia Clarke").age(20).sex("女").build(),
            User.builder().name("Anna Kendrick").age(21).sex("女").build(),
            User.builder().name("Tony").age(21).sex("男").build(),
            User.builder().name("Tom").age(22).sex("男").build(),
            User.builder().name("Jerry").age(23).sex("男").build(),
            User.builder().name("Marks").age(24).sex("男").build()
    );
    Comparator<User> comparator = Comparator.comparing(User::getAge);
    Map<String, Optional<User>> map = list0.stream().collect(Collectors.groupingBy(User::getSex,
            Collectors.reducing(BinaryOperator.maxBy(comparator))));
    map.entrySet().forEach(entry -> {
        System.out.printf("key:  %s + value: %s \n", entry.getKey(), entry.getValue());
    });
    
    // 执行结果
    key:  女 + value: Optional[User(name=Anna Kendrick, sex=女, age=21)] 
    key:  男 + value: Optional[User(name=Marks, sex=男, age=24)] 
  2. Consumer

    消费型接口,且无返回值。

    java 复制代码
    Consumer consumer1 = s -> {
        s += " is good day!";
        System.out.println(s);
    };
    
    Consumer consumer2 = s -> {
        s = "Yesterday is a history, tomorrow is a mystery, but today is a gift, that is why it is called Present.";
        System.out.println(s);
    };
    
    consumer1.andThen(consumer2).accept("Today");
    
    // 执行结果
    Today is good day!
    Yesterday is a history, tomorrow is a mystery, but today is a gift, that is why it is called Present.
  3. Function<T, R>

    函数型接口,接收一个参数,返回一个结果。
    andThen()先执行function1.apply(),再执行function2.apply();compose()则相反。

    java 复制代码
    Function function1 = a -> new StringBuffer(a.toString()).reverse().toString();
    Function function2 = a -> a.toString().toUpperCase();
    String str1 = (String) function1.andThen(function2).apply("hello world");
    String str2 = (String) function1.compose(function2).apply("hello world");
    System.out.printf("str1: %s\n", str1);
    System.out.printf("str2: %s\n", str2);
    // 执行结果
    str1: DLROW OLLEH
    str2: DLROW OLLEH
  4. Predicate

    断言型接口,如果满足条件则返回true,否则返回false。

    java 复制代码
    Predicate predicate1 = s -> String.valueOf(s).length() > 5;
    Predicate predicate2 = s -> String.valueOf(s).startsWith("划");
    boolean flag = predicate1.and(predicate2).test("划过天空的流星");
    System.out.printf("flag: %s", flag);
    
    // 执行结果
    flag: true
  5. Supplier

    供给型接口,提供一个返回值。

    java 复制代码
    Supplier supplier = () -> "爱无悔 情无怨 为何倩影却成烟";
    String str = (String) supplier.get();
    System.out.printf("str: %s", str);
    
    // 执行结果
    str: 爱无悔 情无怨 为何倩影却成烟

Stream流

简介

Stream是一个用于处理集合数据的API,它并不是集合,也不是数据结构,其本身并不存储任何元素(或其他地址值)。元素是特定类型的对象,形成一个队列,Stream并不会存储元素,而是按需计算和处理数据。Stream操作有以下特征:

  • 数据源: 流的来源,可以是集合,数组,I/O资源或者其他数据源等。

  • Pipelining:Stream提供了多种方法针对流中的元素进行处理,这些方法支持链式调用形成流水线式的操作流程。每次操作都会返回一个新的流,原数据保持不变。

  • 延迟处理 :在调用中间操作方法时,不会立即执行实际计算,只有执行终端操作时才会触发实际的计算。

  • 函数式编程:Stream提供了一组函数式编程的方法,可以以声明的方式操作数据,避免了显式的迭代和条件判断,使代码更简洁、易读。

常用方法

当使用一个流的时候,通常包括三个基本步骤:获取一个数据源(source)->数据转换->执行操作获取想要的结果,每次转换原有Stream对象不变,返回一个新的Stream对象(可以有多次转换),这就允许对其操作可以像链条一样排列,变成一个管道。stream流是一种管道流,只能被消费一次,多次消费同一个流会抛出IllegalStateException异常,链式调用是每次将上一个流传递给下一个流,实际上是生成新的流。

  1. Filter(Predicate<? super T> predicate)

    接收一个Predicate函数作为参数,不满足Predicate条件的会被过滤掉。

    java 复制代码
    Stream<String> stream = Stream.of("123", "132", "213", "231", "312", "321");
    stream.filter(e -> e.startsWith("3")).forEach(System.out::println);
    stream.filter(Objects::nonNull);
    
    // 执行结果
    312
    321
    // 多次操作一个流抛出异常
    Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
    	at java.base/java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
    	at java.base/java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:96)
    	at java.base/java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:800)
    	at java.base/java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:167)
    	at java.base/java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:166)
  2. map(Function<? super T, ? extends R> mapper)

    接收一个Function函数作为参数,对Stream中的每个元素进行映射转换,返回新的Stream。

    java 复制代码
    List<String> list0 = Arrays.asList("a", "b", "c", "d", "e", "f");
    list0.stream().map(e -> (int) e.charAt(0)).forEach(System.out::println);
    
    // 执行结果
    97
    98
    99
    100
    101
    102
  3. flatMap(Function<? super T, ? extends Stream<? extends R>> mapper)

    扁平映射,接收一个Function函数将流中每个元素映射为一个流并将所有流连接成一个流。

    java 复制代码
    List<List<Integer>> list1 = Arrays.asList(
            Arrays.asList(1, 2, 3),
            Arrays.asList(4, 5, 6),
            Arrays.asList(7, 8, 9)
    );
    list1.stream().flatMap(List::stream).forEach(System.out::println);
    
    // 执行结果
    123456789
  4. distinct()

    根据流中元素的hashCode()和equals()来判断是否重复,并且去重。

    java 复制代码
    List<Integer> list2 = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 6, 5, 4, 3, 2, 1);
    list2.stream().distinct().forEach(System.out::print);
    
    // 执行结果
    1234567
  5. sorted() / sorted(Comparator<? super T> comparator)

    用于对流中元素进行排序,默认是按照自然顺序排序,对于无序流不提供稳定性保证。
    也可以接收一个Comparator函数自定义排序规则。

    java 复制代码
    List<Integer> list3 = Arrays.asList(5, 3, 1, 4, 2, 6, 7);
    list3.stream().sorted().forEach(System.out::print);
    System.out.println();
    list3.stream().sorted(((o1, o2) -> {
        return o2.compareTo(o1);
    })).forEach(System.out::print);
    
    // 执行结果
    1234567
    7654321
  6. peek(Consumer<? super T> action)

    类似forEach方法,区别在于这不是终端操作,接收一个Consum函数,对流中每一个元素执行一个操作,返回新的流与原始流中的数据相同。

    java 复制代码
    List<Integer> list4 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    list4.stream().peek(e -> {
        e = (e + 3) * 7;
        System.out.printf("value: %d", e);
    }).peek(el -> {
        System.out.printf(" || %d \n", el);
    }).collect(Collectors.toList());
    
    // 执行结果
    value: 28 || 1 
    value: 35 || 2 
    value: 42 || 3 
    value: 49 || 4 
    value: 56 || 5 
    value: 63 || 6 
    value: 70 || 7 
  7. limit(long maxSize)

    截断流中的元素个数。

    java 复制代码
    List<Integer> list5 = Arrays.asList(5, 3, 1, 4, 2, 6, 7);
    list5.stream().limit(3).sorted().forEach(System.out::print);
    
    // 执行结果
    531
  8. skip(long n)

    忽略流中前n个元素。

    java 复制代码
    List<Integer> list6 = Arrays.asList(5, 3, 1, 4, 2, 6, 7);
    list6.stream().skip(3).forEach(System.out::print);
    
    // 执行结果
    4267
  9. forEach(Consumer<? super T> action)

    接收一个Consumer函数,对流中每个元素执行一个操作,这是一个终端操作。

    java 复制代码
    List<Integer> list7 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    list7.stream().forEach(e -> {
        e *= 2;
        System.out.printf("%d ", e);
    });
    
    // 执行结果
    2 4 6 8 10 12 14 
  10. collect(Collector<? super T, A, R> collector)

    接收一个Collector函数,允许重用收集策略和收集操作的组合,这是一个终端操作。

    java 复制代码
    List<Person> list8 = Arrays.asList(
            Person.builder().name("Tom").sex("男").build(),
            Person.builder().name("Jack").sex("男").build(),
            Person.builder().name("Susan").sex("女").build(),
            Person.builder().name("Lilith").sex("女").build()
    );
    Map<String, Map<String, List<Person>>> map = list8.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getName)));
    map.entrySet().forEach(entry -> {
        System.out.printf("%s:%s \n", entry.getKey(), entry.getValue());
    });
    
    // 执行结果
    女:{Lilith=[Person(name=Lilith, sex=女)], Susan=[Person(name=Susan, sex=女)]} 
    男:{Tom=[Person(name=Tom, sex=男)], Jack=[Person(name=Jack, sex=男)]} 
  11. count()

    返回流中元素个数,这是一个终端操作。

    java 复制代码
    List<Integer> list9 = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
    long count = list9.stream().count();
    System.out.printf("count:%d", count);
    
    // 执行结果
    count:7
  12. anyMatch(Predicate<? super T> predicate) / allMatch(Predicate<? super T> predicate) / noneMatch(Predicate<? super T> predicate)

    anyMatch方法用于判断流中元素是否存在至少一个满足给定的条件,返回boolean值,这是一个短路操作[^1]。

    java 复制代码
    List<String> list10 = Arrays.asList("12", "23", "34", "45", "56", "67");
    boolean result = list10.stream().anyMatch(s -> s.length() > 3);
    System.out.printf("result: %s", result);
    
    // 执行结果
    result: false

    allMatch方法用于判断流中元素是否全部满足给定的条件,返回boolean值,这是一个短路操作。

    java 复制代码
    List<String> list10 = Arrays.asList("12", "23", "34", "45", "56", "67");
    result = list10.stream().allMatch(s -> s.length() >= 2);
    System.out.printf("result: %s", result);
    
    // 执行结果
    result: true

    noneMatch方法用于判断流中元素是否没有给定的条件,返回boolean值,这是一个短路操作。

    java 复制代码
    List<String> list10 = Arrays.asList("12", "23", "34", "45", "56", "67");
    result = list10.stream().noneMatch(s -> s.length() > 2);
    System.out.printf("result: %s", result);
    
    // 执行结果
    result: true

    短路操作:对于流中的元素,只要满足条件之后即可返回,无需操作所有元素

Optional

Optional是一个容器类工具类,目的是解决NPE问题。

Optional是一个包装器类,其中包含对其他对象的引用,这样就不用显式的进行空值检测。

创建Optional对象

  1. empty()

    java 复制代码
    Optional<String> empty = Optional.empty();
    System.out.println(empty);
    // 执行结果
    Optional.empty
  2. of(T value)

    此处参数必须不为null,否则会报错NPE;

    java 复制代码
    String str = null;
    Optional<String> stringOptional = Optional.of(str);
    
    // 执行结果
    Exception in thread "main" java.lang.NullPointerException
    	at java.base/java.util.Objects.requireNonNull(Objects.java:208)
    	at java.base/java.util.Optional.of(Optional.java:113)
  3. ofNullable(T value)

    如果参数为null,则会返回空的Optional对象。

    java 复制代码
    String str = null;
    Optional<String> stringOptional = Optional.ofNullable(str);
    System.out.println(stringOptional);
    
    // 执行结果
    Optional.empty

常用方法

  1. isPresent()

    判断一个Optional对象是否存在,如果存在则返回true,否则返回false。

    java 复制代码
    String str = null;
    Optional<String> stringOptional = Optional.ofNullable(str);
    System.out.println(stringOptional.isPresent());
    str = new String("Hello World");
    System.out.println(Optional.ofNullable(str).isPresent());
    
    // 执行结果
    false
    true
  2. isEmpty()

    类似isPresen(),如果对象存在就返回false,否则返回true。

    java 复制代码
    String str = null;
    Optional<String> stringOptional = Optional.ofNullable(str);
    System.out.println(stringOptional.isEmpty());
    str = new String("Hello World");
    System.out.println(Optional.ofNullable(str).isEmpty());
    
    // 执行结果
    true
    false
  3. ifPresent(Consumer<? super T> action)

    接收一个Consumer函数,如果存在值则执行传入的操作。

    java 复制代码
    Optional.ofNullable("Hello World").ifPresent(s -> System.out.printf("%s!", s));
    Optional.ofNullable(null).ifPresent(s -> System.out.printf("%s!", s)); 
    
    // 执行结果
    Hello World!
  4. ifPresentOrElse(Consumer<? super T> action, Runnable emptyAction)

    接收Consumer和Rnnable两个函数,如果存在值就执行第一个,否则就执行第二个操作。

    java 复制代码
    Optional.ofNullable("Hello World").ifPresentOrElse(s -> System.out.printf("%s!\n", s), () -> System.out.println("value不存在"));
    Optional.ofNullable(null).ifPresentOrElse(s -> System.out.printf("%s!", s), () -> System.out.println("value不存在"));
    
    // 执行结果
    Hello World!
    value不存在
  5. filter(Predicate<? super T> predicate)

    接收一个Predicate函数,如果满足条件则返回包含该对象的Optional对象,否则返回一个空的Optional对象。

    java 复制代码
    Optional<String> optional0 = Optional.ofNullable("Hello World").filter(s -> s.length() > 5);
    Optional<String> optional1 = Optional.ofNullable("Hello World").filter(s -> s.length() > 11);
    System.out.printf("optional0: %s\n", optional0);
    System.out.printf("optional1: %s\n", optional1);
    
    // 执行结果
    optional0: Optional[Hello World]
    optional1: Optional.empty
  6. map(Function<? super T, ? extends U> mapper)

    接收一个Function函数,如果值存在则返回一个包含映射结果值的Optional,否则返回一个空的Optional。

    java 复制代码
    Optional<String> nameOptional = Optional.ofNullable(new Person("Jerry", "男")).map(Person::getName);
    System.out.printf("nameOptional: %s", nameOptional);
    
    // 执行结果
    nameOptional: Optional[Jerry]
  7. flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)

    接收一个Function的函数,将 Optional 对象中的值映射为另一个 Optional 对象,然后将两个 Optional 对象合并为一个返回。

    java 复制代码
    Optional<String> nameUpper = Optional.ofNullable(new Person("Jerry", "男")).flatMap(e -> Optional.ofNullable(e.getName().toUpperCase()));
    System.out.printf("nameUpper: %s", nameUpper);
    
    // 执行结果
    nameUpper: Optional[JERRY]
  8. orElse(T other)

    如果Optional的对象是null则返回该值。

    java 复制代码
    String string = (String) Optional.ofNullable(null).orElse("Hello World");
    System.out.printf("string: %s", string);
    
    // 执行结果
    string: Hello World
  9. orElseGet(Supplier<? extends T> supplier)

    接收一个Supplier函数,如果Optional的对象是null则执行该操作。

    java 复制代码
    String name = (String) Optional.ofNullable(null).orElseGet(new Person("Jerry", "男")::getName);
    System.out.printf("name: %s", name);
    
    // 执行结果
    name: Jerry
  10. orElseThrow(Supplier<? extends X> exceptionSupplier)

    接收一个Supplier函数,如果Optional的对象是null则抛出自定义异常。

    java 复制代码
    try {
        Optional.ofNullable(null).orElseThrow(() -> new Exception("npe"));
    }catch (Exception e) {
        e.printStackTrace();
    }
    
    // 执行结果
    java.lang.Exception: npe
相关推荐
wainyz1 分钟前
Java NIO操作
java·开发语言·nio
工业3D_大熊7 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
lzb_kkk15 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
爬山算法39 分钟前
Maven(28)如何使用Maven进行依赖解析?
java·maven
2401_857439691 小时前
SpringBoot框架在资产管理中的应用
java·spring boot·后端
怀旧6661 小时前
spring boot 项目配置https服务
java·spring boot·后端·学习·个人开发·1024程序员节
李老头探索1 小时前
Java面试之Java中实现多线程有几种方法
java·开发语言·面试
芒果披萨1 小时前
Filter和Listener
java·filter
qq_4924484461 小时前
Java实现App自动化(Appium Demo)
java
阿华的代码王国1 小时前
【SpringMVC】——Cookie和Session机制
java·后端·spring·cookie·session·会话