JDK8函数式编程快速入门干货

本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

原文链接:blog.ouyangsihai.cn >> JDK8函数式编程快速入门干货

Java8引入函数式编程,极大地提高了编码效率,对于习惯了原有Java代码风格的coder,确实需要好好熟悉一番才能熟练地coding。
本文每一条讲解都配置了最简短的代码,适合快速入门或者字典使用,但阅读之后还是需要多看函数原型多看源码,灵活使用为好。

(排版简陋,如果想看格式整齐的版本,请点击文末的阅读原文)

本文目录: 

  • 函数式编程语法 
  • 常见的Java函数式接口
    • Consumer 
    • Supplier 
    • Function 
    • Predicate 
    • BinaryOperator 
    • UnaryOperator 
    • 常见的Java函数式接口

      Supplier 

      Predicate 

      UnaryOperator 

      函数式编程接口的使用

    • Stream类
      • Stream对象的创建
        • 创建空的Stream对象 
        • 通过集合类中的stream或者parallelStream方法创建 
        • 通过Stream中的of方法创建 
        • 通过Stream中的iterate方法创建 
        • 通过Stream中的generate方法创建 
        • 通过Stream中的concat方法连接两个Stream对象生成新的Stream对象 
        • Stream对象的创建

          通过集合类中的stream或者parallelStream方法创建 

          通过Stream中的iterate方法创建 

          通过Stream中的concat方法连接两个Stream对象生成新的Stream对象 

          Stream对象的函数列表 

          常用函数示例

        • map 
        • flatMap 
        • filter 
        • reduce 
        • collect 
        • flatMap 

          reduce 

          Optional类

        • Optioanl对象的创建
          • empty 
          • of 
          • ofNullable 
          • empty 

            ofNullable 

            Optional的使用

          • 常用函数列表 
          • 简单示例
            • 变量为空,则提供默认值 
            • 如果变量为空时,抛异常的话 
            • 如果变量为不为空则可以使用 
            • 如果变量不为空则返回一定规则的返回值 
            • 简单示例

              如果变量为空时,抛异常的话 

              如果变量不为空则返回一定规则的返回值 

              函数式编程语法

              以Consumer作为示例,它是一个函数式接口,包含一个抽象方法accept。现在要定义一个Consumer对象,传统方式是这样的:

                  Consumer c = new Consumer() {

                      @Override

                      public void accept(Object o) {

                          System.out.println(o);

                      }

                  };

              而使用函数式编程,可以这样定义:

                  Consumer c = (o) - {

                      System.out.println(o);

                  }; 

              亦或者:

                  Consumer c = (o) - System.out.println(o);

              简单理解函数式对象本质上还是一个对象,可以看做是其他方法的参数或者返回值,所以上面代码实际上可以这样:

                  Consumer c = System.out::println;

              常见的Java函数式接口

              Consumer

              顾名思义,Consumer的意思就是消费,即针对某个东西我们来使用它,因此它包含一个有形参无返回值的accept接口方法。 除了accept方法,还包含andThen这个方法,可以连续调用多个Consumer

              public static void consumerTest() {

                  Consumer f1 = System.out::println;

                  Consumer f2 = n - System.out.println(n + “-F2”);

                  //执行f2之后,连续执行两次f2Accept方法

                  f2.andThen(f1).andThen(f1).accept(“test1”);

              }

              Supplier

              Supplier 代表的含义是“提供者”,因此它含有一个get方法,没有入参只能输出

              SupplierString supplier = () - “Test supplier”;

              supplier.get();

              Function

              Function也是一个函数式编程接口;它代表的含义是“函数”,而函数经常是有输入输出的,因此它含有一个apply方法,包含一个入参与一个返回值,可以用作装箱或者拆箱某个对象

              /**

              ** * Function**测试

              ** /*

              public static void functionTest() {

                  Function****Integer, Integer f = s - s++;

                  Function****Integer, Integer g = s - s * 2;

                  /**

              **     * 下面表示在执行F时,先执行G,并且执行F时使用G**的输出当作输入。

              **     * **相当于以下代码:

              **     * Integer a = g.apply(1);**

              **     * System.out.println(f.apply(a));**

              **     /*

                  System.out.println(f.compose(g).apply(1));

                  /**

              **     * 表示执行FApply后使用其返回的值当作输入再执行GApply**;

              **     * **相当于以下代码

              **     * Integer a = f.apply(1);**

              **     * System.out.println(g.apply(a));**

              **     /*

                  System.out.println(f.andThen(g).apply(1));

                  /**

              **     * identity方法会返回一个不进行任何处理的Function,即输出与输入值相等; **

              **     /*

                  System.out.println(Function.identity().apply(“a”));

              }

              Predicate

              Predicate为函数式接口,predicate的中文意思是“断定”,意为判断某个东西是否满足某种条件;因此它包含test方法,根据输入值来做逻辑判断,其结果为True或者False,可以用作过滤对象

              /**

               * Predicate测试

               */

              private static void predicateTest() {

                  PredicateString p = o - o.equals(“test”);

                  PredicateString g = o - o.startsWith(“t”);

                  /**

                   * negate: 用于对原来的Predicate做取反处理;

                   * 如当调用p.test(“test”)为True时,调用p.negate().test(“test”)就会是False;

                   */

                  Assert.assertFalse(p.negate().test(“test”));

                  /**

                   * and: 针对同一输入值,多个Predicate均返回True时返回True,否则返回False;

                   */

                  Assert.assertTrue(p.and(g).test(“test”));

                  /**

                   * or: 针对同一输入值,多个Predicate只要有一个返回True则返回True,否则返回False

                   */

                  Assert.assertTrue(p.or(g).test(“ta”));

              }

              BinaryOperator

              BinaryOperator接口用于执行lambda表达式,接受两个T类型的参数并返回一个T类型的返回值。BinaryOperator中有两个静态方法,是用于比较两个数字或字符串的大小

              minBy()获取更小的值,maxBy()获取更大的值。示例如下:

              BinaryOperatorInteger add = (n1, n2) - n1 + n2;

              System.out.println(add.apply(1, 2)); //3

              System.out.println(BinaryOperator.maxBy(Integer::compareTo).apply(1,2));//2

              UnaryOperator

              UnaryOperator继承于java.util.function.Function。UnaryOperator接收一个T类型参数,并返回T类型的参数

              UnaryOperatorString ddb = x - x + 1;

              System.out.println(ddb.apply(“abc”));// abc1

              函数式编程接口的使用

              Stream

              Java 8 中,引入了流(Stream)的概念,这个流和以前我们使用的 IO 中的流并不太相同。所有继承自 Collection 的接口都可以转换为 Stream。

              Stream 的方法分为两类。一类叫惰性求值,一类叫及早求值。

              判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是 Stream,那么是惰性求值。其实可以这么理解,如果调用惰性求值方法,Stream 只是记录下了这个惰性求值方法的过程,并没有去计算,等到调用及早求值方法后,就连同前面的一系列惰性求值方法顺序进行计算,返回结果。

              Stream.惰性求值.惰性求值. … .惰性求值.及早求值

              Stream对象的创建

              创建空的Stream对象

              **Stream stream **= Stream.empty();

              通过集合类中的stream或者parallelStream方法创建

              ListString list = Arrays.asList(“a”, “b”, “c”, “d”);

              Stream listStream = list.stream();  //获取串行的Stream对象

              Stream parallelListStream = list.parallelStream(); //获取并行的Stream对象

              Stream具有平行处理能力,处理的过程会分而治之,也就是将一个大任务切分成多个小任务,这表示每个任务都是一个操作。所以parallelListStream.forEach(out::println)的输出结果不一定是abcd。如果想要按照原本的stream顺序,可以使用parallelListStream.forEachOrdered(out::println)

              通过Stream中的of方法创建

              Stream s = Stream.of(“a”, “b”, “c”, “d”);

              通过Stream中的iterate方法创建

              Stream.iterate(1, item - item + 1).limit(10).forEach(System.out::println);

              这段代码就是先获取一个无限长度的正整数集合的Stream,然后取出前10个打印。千万记住使用limit方法,不然会无限打印下去。

              通过Stream中的generate方法创建

               Stream.generate(() - {

                          System.out.println(“test”);

                          return Math.random();

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

              通过Stream中的concat方法连接两个Stream对象生成新的Stream对象

              concat方法将两个Stream连接在一起,合成一个Stream。若两个输入的Stream都是串行的,则新Stream也是串行的;若输入的Stream中任何一个是并行的,则新的Stream也是并行的

              Stream.concat(Stream.of(1, 2, 3), Stream.of(4, 5))

                     .forEach(integer - System.out.print(integer + “  “));// 1  2  3  4  5

              Stream对象的函数列表

              Stream对象提供多个非常有用的方法,这些方法可以分成两类:

              中间操作:将原始的Stream转换成另外一个Stream;如filter返回的是过滤后的Stream。
              终止操作:产生的是一个结果或者其它的复合操作;如count或者forEach操作。

              方法都列举出来,但不是要求倒背如流,只是希望不重复造轮子

              中间操作列表:

              方法名说明

              说明

              sequential返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象

              返回一个相等的串行的Stream对象,如果原Stream对象已经是串行就可能会返回原对象

              parallel返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象

              返回一个相等的并行的Stream对象,如果原Stream对象已经是并行的就会返回原对象

              unordered返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象

              返回一个不关心顺序的Stream对象,如果原对象已经是这类型的对象就会返回原对象

              onClose返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象

              返回一个相等的Steam对象,同时新的Stream对象在执行Close方法时会调用传入的Runnable对象

              close关闭Stream对象

              关闭Stream对象

              mapToInt元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象

              元素一对一转换:将原Stream中的使用传入的IntFunction加工后返回一个IntStream对象

              distinct去重:返回一个去重后的Stream对象

              去重:返回一个去重后的Stream对象

              sorted排序:返回排序后的Stream对象

              排序:返回排序后的Stream对象

              peek使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象

              使用传入的Consumer对象对所有元素进行消费后,返回一个新的包含所有原来元素的Stream对象

              limit获取有限个元素组成新的Stream对象返回

              获取有限个元素组成新的Stream对象返回

              skip抛弃前指定个元素后使用剩下的元素组成新的Stream返回

              抛弃前指定个元素后使用剩下的元素组成新的Stream返回

              takeWhile如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream

              如果Stream是有序的(Ordered),那么返回最长命中序列(符合传入的Predicate的最长命中序列)组成的Stream;如果是无序的,那么返回的是所有符合传入的Predicate的元素序列组成的Stream

              dropWhile与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream

              与takeWhile相反,如果是有序的,返回除最长命中序列外的所有元素组成的Stream;如果是无序的,返回所有未命中的元素组成的Stream

              终止操作列表:

              方法名说明

              说明

              iterator返回Stream中所有对象的迭代器

              返回Stream中所有对象的迭代器

              spliterator返回对所有对象进行的spliterator对象

              返回对所有对象进行的spliterator对象

              forEach对所有元素进行迭代处理,无返回值

              对所有元素进行迭代处理,无返回值

              forEachOrdered按Stream的Encounter所决定的序列进行迭代处理,无返回值

              按Stream的Encounter所决定的序列进行迭代处理,无返回值

              toArray返回所有元素的数组

              返回所有元素的数组

              min返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty

              返回所有元素中最小值的Optional对象;如果Stream中无任何元素,那么返回的Optional对象为Empty

              max与min相反

              与min相反

              count所有元素个数

              所有元素个数

              anyMatch只要其中有一个元素满足传入的Predicate时返回True,否则返回False

              只要其中有一个元素满足传入的Predicate时返回True,否则返回False

              allMatch所有元素均满足传入的Predicate时返回True,否则False

              所有元素均满足传入的Predicate时返回True,否则False

              noneMatch所有元素均不满足传入的Predicate时返回True,否则False

              所有元素均不满足传入的Predicate时返回True,否则False

              findFirst返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回

              返回第一个元素的Optioanl对象;如果无元素返回的是空的Optional; 如果Stream是无序的,那么任何元素都可能被返回

              findAny返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl

              返回任意一个元素的Optional对象,如果无元素返回的是空的Optioanl

              isParallel判断是否当前Stream对象是并行的

              判断是否当前Stream对象是并行的

              常用函数示例

              map

              接收一个Funcation参数,用其对Stream中的所有元素进行处理,返回的Stream对象中的元素为Function对原元素处理后的结。示例如下:

              StreamString s = Stream.of( “t1”, “t2”);

              s.map(n - n.concat(“.txt”)).forEach(System.out::println);// ti.txt t2.txt

              flatMap

              元素一对多转换:对原Stream中的所有元素使用传入的Function进行处理,每个元素经过处理后生成一个多个元素的Stream对象,然后将返回的所有Stream对象中的所有元素组合成一个统一的Stream并返回;

               StreamString s = Stream.of(“1t1”, “2t2”);

                      s.flatMap(n -Stream.of(n.split(“t”))).forEach(System.out::println); //1 1 2 2

              filter

              用于对Stream中的元素进行过滤,返回一个过滤后的Stream

              StreamString s = Stream.of(“t1”, “t2”, “aaa”);

                      s.filter(n - n.contains(“t”)).forEach(System.out::println); //t1 t2

              reduce

              map用来归类,结果一般是一组数据,比如可以将list中的学生分数映射到一个新的stream中;reduce用来计算值,结果是一个值,比如计算最高分

              ListPerson personList = Arrays.asList(

                              new Person(1, 18, “小明”),

                              new Person(1, 19, “小强”),

                              new Person(1, 20, “小刚”),

                              new Person(1, 19, “小王”))

                              .stream()

                              .collect(Collectors.toList());

                      personList.stream()

                              .reduce((person1, person2) - person1.getAge() person2.getAge() ? person1 : person2

                              )

                              .ifPresent((person) - System.out.println(JSON.toJSON(person)));

              collect

              可以将stream元素转换为不同类型的结果(List, Set , Map)。转list用Collectors.toList();转set使用Collectors.toSet()。 示例(转List):

              ListPerson personList = Arrays.asList(

                              new Person(1, 18, “小明”),

                              new Person(1, 19, “小强”),

                              new Person(1, 19, “小王”))

                              .stream()

                              .filter(person - {

                                  return person.getAge() == 19;

                              })

                              .collect(Collectors.toList());

                      personList.forEach(person - System.out.println(person.getName()));

              示例(转map):

              MapInteger, ListPerson personMap = Arrays.asList(

                              new Person(1, 18, “小明”),

                              new Person(1, 19, “小强”),

                              new Person(1, 19, “小王”))

                              .stream()

                              .collect(Collectors.groupingBy(p-p.getAge()));

                      personMap.forEach((age,person)- System.out.println(age+”:”+ JSON.toJSON(person)));

              Optional

              这个类主要用来简化对空值的判断处理。Optional实际上是对一个变量进行封装,它包含有一个属性value,实际上就是这个变量的值。

              Optioanl对象的创建

              它的默认构造函数是private类型的,因此要初始化一个Optional的对象无法通过其构造函数进行创建。它提供了一系列的静态方法用于构建Optional对象:

              empty

              用于穿见一个空的Optional对象,其value属性为null。例如:

                  Optional o = Optional.empty();

              of

              根据传入的值构建一个Optional对象。传入的值必须是非空值,否则如果传入的值为空值,则会抛出空指针异常。例如:

                  o = Optional.of(“test”);

              ofNullable

              根据传入值构建一个Optional对象。传入的值可以是空值,如果传入的值是空值,则与empty返回的结果是一样的。

              Optional的使用

              常用函数列表

              方法名说明

              说明

              get获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异常;因此返回的Value值无需再做空值判断,只要没有抛出异常,都会是非空值

              获取Value的值,如果Value值是空值,则会抛出NoSuchElementException异常;因此返回的Value值无需再做空值判断,只要没有抛出异常,都会是非空值

              isPresentValue是否为空值的判断

              Value是否为空值的判断

              ifPresent当Value不为空时,执行传入的Consumer

              当Value不为空时,执行传入的Consumer

              ifPresentOrElseValue不为空时,执行传入的Consumer;否则执行传入的Runnable对象;

              Value不为空时,执行传入的Consumer;否则执行传入的Runnable对象;

              filter当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回当前的Optional对象

              当Value为空或者传入的Predicate对象调用test(value)返回False时,返回Empty对象;否则返回当前的Optional对象

              map一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的结果组装的Optional对象;

              一对一转换:当Value为空时返回Empty对象,否则返回传入的Function执行apply(value)后的结果组装的Optional对象;

              flatMap一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的结果(其返回结果直接是Optional对象)

              一对多转换:当Value为空时返回Empty对象,否则传入的Function执行apply(value)后返回的结果(其返回结果直接是Optional对象)

              or如果Value不为空,则返回当前的Optional对象;否则,返回传入的Supplier生成的Optional对象;

              如果Value不为空,则返回当前的Optional对象;否则,返回传入的Supplier生成的Optional对象;

              stream如果Value为空,返回Stream对象的Empty值;否则返回Stream.of(value)的Stream对象;

              如果Value为空,返回Stream对象的Empty值;否则返回Stream.of(value)的Stream对象;

              orElseValue不为空则返回Value,否则返回传入的值;

              Value不为空则返回Value,否则返回传入的值;

              orElseGetValue不为空则返回Value,否则返回传入的Supplier生成的值;

              Value不为空则返回Value,否则返回传入的Supplier生成的值;

              orElseThrowValue不为空则返回Value,否则抛出Supplier中生成的异常对象;

              Value不为空则返回Value,否则抛出Supplier中生成的异常对象;

              简单示例

              变量为空,则提供默认值

               MapInteger, String map = new HashMap();

                  map.put(1, “大米”);

                  map.put(2, “小米”);

                  map.put(3, “黑米”);

                  String name = Optional.ofNullable(map.get(4)).orElse(“none”);

                  System.out.println(name);  //none

              用原本的代码写,就是这样的:

                  if (map.get(4) == null) {

                      return “none”;

                  } else {

                      return map.get(4);

                  }

              如果变量为空时,抛异常的话

                  Optional.ofNullable(map.get(4)).orElseThrow(()-new Exception(“test”));

              如果变量为不为空则可以使用

              按照原来的写法,如果不判断变量是否为空,并调用变量的成员方法的话,函数会抛空指针异常

                  Optional.ofNullable(map.get(4)).ifPresent(System.out::println);

              如果变量不为空则返回一定规则的返回值

                  return user.map(u - u.getUsername())

                         .map(name - name.toUpperCase())

                         .orElse(null);

              上面这段转换成平时的Java代码,则变成这样:

                  User user = new User….  

                  if(user != null) {

                    String name = user.getUsername();

                    if(name != null) {

                      return name.toUpperCase();

                    } else {

                      return null;

                    }

                  } else {

                    return null;

                  }

              (哇,4000字的博客你都看得完。。。)

              原文始发于微信公众号(全菜工程师小辉):JDK8函数式编程快速入门干货

    本人花费半年的时间总结的《Java面试指南》已拿腾讯等大厂offer,已开源在github ,欢迎star!

    本文GitHub https://github.com/OUYANGSIHAI/JavaInterview 已收录,这是我花了6个月总结的一线大厂Java面试总结,本人已拿大厂offer,欢迎star

    原文链接:blog.ouyangsihai.cn >> JDK8函数式编程快速入门干货


     上一篇
    全解史上最快的JOSN解析库 – alibaba Fastjson 全解史上最快的JOSN解析库 – alibaba Fastjson
    点击上方“后端技术精选”,选择“置顶公众号” 技术文章第一时间送达! 作者:jajian cnblogs.com/jajian/p/10051901.html cnblogs.com/jajian/p&#
    下一篇 
    Java8新特性——方法引用详解 Java8新特性——方法引用详解
      致力于最高效的Java学习 Java8 引入了方法引用特性,使用它可以简化 Lambda 表达式,我们知道 Lambda 是用来替代匿名类的,即使用 Lambda 可以更加方便地实现函数接口的方法,如下所示。 1、自定义函数接