WZX's blog 海滩上捡贝壳的孩子

Java8中的函数式编程和流式编程

2020-07-13
wzx
 

Java8的新特性,函数式编程和流式操作与Scala很相似。

函数式编程

OO(object oriented)是抽象数据,FP(functional programming)是抽象行为,函数式编程具有以下特点:

  • 代码创建复用:通过合并现有代码来生成新功能而不是从头开始编写所有内容
  • 并行编程,可靠性:不可变的变量,将值传递给函数,该函数然后生成新值但不修改自身外部的任何东西(包括其参数或该函数范围之外的元素)

语法糖

  • lambda表达式:匿名函数,可以替代匿名内部类
    • (params) -> {function body}
  • 方法引用:object::methodClass::method

函数接口

  • java.util.function包下提供了默认的函数接口
  • @FunctionalInterface标记接口,编译器会检测该接口是否只有一个函数式方法(抽象方法),作为Lambda 表达式和方法引用的目标类型

常见接口

  • Predicate – 传入一个参数,返回一个bool结果。函数式方法为boolean test(T t);
  • Consumer – 传入一个参数,无返回值。 函数式方法为void accept(T t);
  • Function – 传入一个参数,返回一个结果。函数式方法为void accept(T t);
  • BiFunction – 传入两个参数,类型不同。函数式方法为R apply(T t, U u);
  • Supplier – 无参数传入,返回一个结果。函数式方法为T get();
  • UnaryOperator – 一元操作符,继承Function,传入参数的类型和返回类型相同。函数式方法为T apply(T t);
  • BinaryOperator – 二元操作符,继承BiFunction,传入的两个参数的类型和返回类型相同。函数式方法为T apply(T t, T u);

闭包

当函数使用了作用域之外的变量时,由函数及其相关的引用环境组合而成的实体就是闭包

  • lambda表达式中引用了局部变量时,局部变量具有final或者等同final效果
  • lambda表达式中引用了对象的属性时,属性拥有独立生命周期,所以不需要强制final修饰
public class Main {
    int field;
    // IntSupplier makeFun(final int x) {
    IntSupplier makeFun(int x) {
        // final int variable = 0;
        int variable = 0;

        // compile error
        // return () -> x++ + variable++;
        return () -> x + field++;
    }
}

高阶函数

用于消费或者产生函数的函数

常见的组合函数就是高阶函数

  • andThen(argument):先执行调用者,再执行参数
  • compose(argument):先执行参数,再执行调用者
  • and(argument)
  • or(argument)
  • negate()

柯里化

将一个多参数的函数,转换为一系列单参数函数

// 柯里化函数
Function<String, Function<String, String>> sum =
    a -> b -> a + b;
// 部分应用
Function<String, String> a = sum.apply("a")
// res: a1
a.apply("1")
// res: a2
a.apply("2")

流式编程

流是一系列与特定存储机制无关的元素。流可以使程序短小精悍,当应用 Lamda 表达式和方法引用时,更加地优雅。当使用parallel()时,可以将流分割为多个,并在不同处理器上分别执行操作,提升效率。

流创建

// 创建对象流,流中的元素都是对象
Stream.of(1, 2, 3);
Arrays.asList(1, 2, 3).stream();

// 创建数字流,流中的元素都是基本类型
IntStream.rangeClosed(1, 3);
IntStream.of(1, 2, 3);
IntStream ints = new Random().ints();

// 将数字流包装成对象流
ints.boxed();

// builder
IntStream.builder().add(1).add(2).build();
// iterate
Stream.iterate(1, i -> i + 1);	// 1, f(1), f(f(1)), ...
// generate
Stream.generate(() -> 1);	// 1, 1, 1, ...

中间操作

  • Peek:消费元素,但不改变流,适用于调试
  • sorted(Comparator):排序
  • distinct:去重
  • filter(Predicate):过滤
  • map(Function),mapToInt(Function),…:应用函数到元素
  • flatMap(Function),flatMapToInt(Function),…:组合流
  • skip(n):跳过
  • limit(n):截断

终端操作

  • 数组
    • toArray
    • toArray(generate)
  • 循环
    • forEach(Consumer)
    • forEachOrdered(Consumer):按原始流顺序迭代
  • 集合
    • collect(Collector):由收集器收集流元素至集合中
    • collect(Supplier, BiConsumer, Biconsumer):第一个参数 Supplier 创建了一个新结果集合,第二个参数 BiConsumer 将下一个元素包含到结果中,第三个参数 BiConsumer 用于将两个值组合起来
  • 组合
    • reduce(BinaryOperator)
    • reduce(T, BinaryOperator):T`为初始值
  • 匹配
    • allMatch(Predicate) :如果流的每个元素根据提供的 Predicate 都返回 true 时,结果返回为 true。在第一个 false 时,则停止执行计算。
    • anyMatch(Predicate):如果流中的任意一个元素根据提供的 Predicate 返回 true 时,结果返回为 true。在第一个 false 是停止执行计算
    • noneMatch(Predicate):如果流的每个元素根据提供的 Predicate 都返回 false 时,结果返回为 true。在第一个 true 时停止执行计算
  • 查找
    • findFirst():返回第一个流元素的 Optional,如果流为空返回 Optional.empty
    • findAny(:返回含有任意流元素的 Optional,如果流为空返回 Optional.empty
  • 信息
    • 对象流
      • count()
      • max(Comparator)
      • min(Comparator)
    • 数字流
      • average()
      • max()
      • min()
      • sum()

REFERENCE

[1] On Java8


上一篇 Scala-模式匹配

Comments

Content