• java8新特性——Function&Stream&Optional


    java8新特性——Function&Stream&Optional

    函数式编程

    简介

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

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

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

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

    常用的函数式接口

    1. BinaryOperator extends BiFunction

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      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)] 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
      • 17
      • 18
      • 19
      • 20
    2. Consumer

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

      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.
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
    3. Function

      函数型接口,接收一个参数,返回一个结果。

      andThen()先执行function1.apply(),再执行function2.apply();compose()则相反。

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    4. Predicate

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    5. Supplier

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

      Supplier supplier = () -> "爱无悔 情无怨 为何倩影却成烟";
      String str = (String) supplier.get();
      System.out.printf("str: %s", str);
      
      // 执行结果
      str: 爱无悔 情无怨 为何倩影却成烟
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    Stream流

    简介

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

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

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

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

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

    常用方法

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

    1. Filter(Predicate predicate)

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

      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)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    2. map(Function mapper)

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    3. flatMap(Function> mapper)

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    4. distinct()

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
    5. sorted() / sorted(Comparator comparator)

      用于对流中元素进行排序,默认是按照自然顺序排序,对于无序流不提供稳定性保证。

      也可以接收一个Comparator函数自定义排序规则。

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
    6. peek(Consumer action)

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

      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 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
      • 15
      • 16
    7. limit(long maxSize)

      截断流中的元素个数。

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

      忽略流中前n个元素。

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

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

      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 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    10. collect(Collector collector)

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

      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=)]} 
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
      • 10
      • 11
      • 12
      • 13
      • 14
    11. count()

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    12. anyMatch(Predicate predicate) / allMatch(Predicate predicate) / noneMatch(Predicate predicate)

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

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

    Optional

    Optional是一个容器类工具类,目的是解决NPE问题。
    Optional是一个包装器类,其中包含对其他对象的引用,这样就不用显式的进行空值检测。

    创建Optional对象

    1. empty()

      Optional<String> empty = Optional.empty();
      System.out.println(empty);
      // 执行结果
      Optional.empty
      
      • 1
      • 2
      • 3
      • 4
    2. of(T value)

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

      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)
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
    3. ofNullable(T value)

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

      String str = null;
      Optional<String> stringOptional = Optional.ofNullable(str);
      System.out.println(stringOptional);
      
      // 执行结果
      Optional.empty
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6

    常用方法

    1. isPresent()

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    2. isEmpty()

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
      • 9
    3. ifPresent(Consumer action)

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

      Optional.ofNullable("Hello World").ifPresent(s -> System.out.printf("%s!", s));
      Optional.ofNullable(null).ifPresent(s -> System.out.printf("%s!", s)); 
      
      // 执行结果
      Hello World!
      
      • 1
      • 2
      • 3
      • 4
      • 5
    4. ifPresentOrElse(Consumer action, Runnable emptyAction)

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

      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不存在
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
    5. filter(Predicate predicate)

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

      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
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
    6. map(Function mapper)

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

      Optional<String> nameOptional = Optional.ofNullable(new Person("Jerry", "男")).map(Person::getName);
      System.out.printf("nameOptional: %s", nameOptional);
      
      // 执行结果
      nameOptional: Optional[Jerry]
      
      • 1
      • 2
      • 3
      • 4
      • 5
    7. flatMap(Function> mapper)

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

      Optional<String> nameUpper = Optional.ofNullable(new Person("Jerry", "男")).flatMap(e -> Optional.ofNullable(e.getName().toUpperCase()));
      System.out.printf("nameUpper: %s", nameUpper);
      
      // 执行结果
      nameUpper: Optional[JERRY]
      
      • 1
      • 2
      • 3
      • 4
      • 5
    8. orElse(T other)

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

      String string = (String) Optional.ofNullable(null).orElse("Hello World");
      System.out.printf("string: %s", string);
      
      // 执行结果
      string: Hello World
      
      • 1
      • 2
      • 3
      • 4
      • 5
    9. orElseGet(Supplier supplier)

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

      String name = (String) Optional.ofNullable(null).orElseGet(new Person("Jerry", "男")::getName);
      System.out.printf("name: %s", name);
      
      // 执行结果
      name: Jerry
      
      • 1
      • 2
      • 3
      • 4
      • 5
    10. orElseThrow(Supplier exceptionSupplier)

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

      try {
          Optional.ofNullable(null).orElseThrow(() -> new Exception("npe"));
      }catch (Exception e) {
          e.printStackTrace();
      }
      
      // 执行结果
      java.lang.Exception: npe
      
      • 1
      • 2
      • 3
      • 4
      • 5
      • 6
      • 7
      • 8
  • 相关阅读:
    多线程拷贝文件
    现代 CSS 解决方案:文字颜色自动适配背景色!
    【Vue-Element-Admin】table排序
    记:lorawan协议
    制作自己的软渲染器(二) 顶点缓冲与插值
    裸金属服务器是什么
    【Truffle】四、通过Ganache部署连接
    最短路径算法-迪杰斯特拉(Dijkstra)算法在c#中的实现和生产应用
    Scss--@extend--使用/实例
    Unity --- 脚本组件 --- 生命周期与执行顺序
  • 原文地址:https://blog.csdn.net/SGdan_qi/article/details/133850213