Skip to content

Latest commit

 

History

History
276 lines (182 loc) · 9.79 KB

函数式编程.md

File metadata and controls

276 lines (182 loc) · 9.79 KB

函数式编程

函数式编程的编程特性

* 不可变
* 惰性求值
* 闭包
* 高阶函数
* 柯里化
* 部分应用
* 结合律

Lambda 表达式

  • 参数类型可推导
  • 单行可省略单括号
  • 单参数可以省略小括号
(int a, int b) -> {return a + b;}
(a, b) -> a + b;
a -> a * a;
() -> return true;

函数式接口(SAM Single Abstract Method)

@FunctionalInterface // 非必需,加上该注解编译器会进行校验
public interface Runnable {
    // SAM 只能是单个的非默认方法,这是由于 Java 8 的 interface 允许在接口内定义默认的方法体和静态方法
    public abstract void run();
}

常用函数式接口对象:

Function<T, R> :一个输入参数与返回值,将 T 转换为 R。SAM 为 R applay(T t)

Predicate<T> :一个输入参数,返回一个 boolean 值。SAM 为 boolean test(T t)

Consumer<T> :一个输入参数 ,执行对应的行为。SAM 为 void accept(T t)

Supplier<T> : 无输入参数,返回一个对应的泛型 T 对象,类似于一个工厂方法。SAM 为 T get()

UnaryOperator<T> :一个一元操作,将 输入的 T 转变为 T,该接口继承自 Funtion<T, R>, 因此 SAM 也为 R apply(T t)

BinaryOperator<T, U, R> :一个二元操作函数,输入第一个参数T 和 第二个参数U ,得到处理结果 R。该接口继承自 BiFunction<T, U, R> ,SAM 为 R apply(T t, U u)

方法引用:

  • 类的静态方法:ClassX::methodName
  • 类的构造方法:ClassX::new
  • 类的实例方法:instance::methodName
  • 对于类的实例方法,当实例方法所需参数小于 Lambda 要求的参数时,将会把当前的实例对象作为一个参数放入 Lambda 中作为参数。。。。。(具体细节未知)

函数式接口转换:

  • 忽略输入:Supplier -> Function
  • 忽略返回:Function -> Consumer
  • 忽略输入和返回:Supplier -> Runnable

void-compatibility 规则:如果 Lambda 是一个语句表达式,那么即时该 Lambda 有返回值也可以赋值给返回值为 void 的函数

Stream

​ 相比较于一般的 List 容器,Stream 存在以下这些优点:

  • Stream 可以是无限的
  • Stream 可以对数据进行并行处理
  • Stream 可以延迟执行

Functionl Programming 的三个主要方法

filter(Predicate<? super T> predicate) 通过传入的 Predicate 函数对数据进行过滤。该方法是一个中间操作方法。

map(Function<? super T, ? extends R> mapper) 通过传入的 mapper 映射函数对数据元素进行转换。该方法也是一个中间操作方法。

reduce(T identity, BinaryOperator<T> accumulator) 通过传入的二元函数对初始数据元素为 identity 的数据进行 reduction 操作。该方法是一个终止操作方法。

reduce() 方法的示例:

  • 数据求和

    public static int sum(List<Integer> list) {
    	// 从流中每次计算得到两个数据的总和,直到所有数据都被处理
        return list.stream().reduce(0, Integer::sum);
    }
  • 求最大值与最小值

    public static int max(List<Integer> list) {
        return list.stream().reduce(Integer.MIN_VALUE, Math::max);
    }
    
    public static int min(List<Integer> list) {
        return list.stream().reduce(Integer.MAX_VALUE, Math::min);
    }
  • 组合成字符串

    public static String join(List<Integer> list) {
        return list.stream().map(String::valueOf)
                .reduce((String s1, String s2) -> String.join(", ", s1, s2))
                .orElse(" ");
    }
  • 将数据存放到容器中

    public static <T> List<T> toList(Stream<T> stream) {
        List<T> tmp = new ArrayList<>(); // 最终保存结果的列表对象
        return stream.reduce(tmp, (List<T> t1, T obj) -> {
            t1.add(obj);
            return t1;
        }, (List<T> t1, List<T> t2) -> {
            t1.addAll(t2);
            return t2;
        }// 整合规则);
    }
  • 使用 reduce 实现 map

    public static <T, R> List<R> toMap(List<T> list, Function<T, R> transfer) {
        List<R> rList = new ArrayList<>(); // 存放结果的容器对象
        return list.stream().reduce(rList, (List<R> r1, T obj) -> {
            R r = transfer.apply(obj);
            r1.add(r);
            return r1; // 传入得到数据元素使用转换函数进行转换,添加到当前处理的容器对象中
        }, (List<R> r1, List<R> r2) -> {
            r1.addAll(r2);
            return r1; // 容器元素的整合规则
        });
    }
  • 使用 reduce 实现 filter

    public static <T> List<T> toFilter(List<T> list, Predicate<T> filter) {
        List<T> tmp = new ArrayList<>(); // 存储结果的容器对象
        return list.stream().reduce(tmp, (List<T> t1, T obj) -> {
            if (filter.test(obj)) t1.add(obj); // 元素过滤规则
            return t1;
        }, (List<T> t1, List<T> t2) -> {
            t1.addAll(t2);
            return t1;
        }); // 容器的整合规则
    }

Stream.collect()

Stream.collect() 相比较于 Stream.reduce() , collect() 操作可变数据,reduce() 操作不可变数据

  • <R> R collect(Supplier<R> supplier, // 一个创建新的可变结果容器的函数,对于并行的执行,该函数可能会被多次调用,每次返回更新后的值
                      BiConsumer<R, ? super T> accumulator, // 将第二个参数对象折叠到第一个容器中的二元函数
                      BiConsumer<R, R> combiner); // 将第二个容器的所有元素折叠到第一个容器中的二元函数
  • <R, A> R collect(Collector<? super T, A, R> collector); // Collector 聚集了上文第一个方法需要的函数

其中,Collector 包含了所需的函数,包括:

  • Supplier<A> supplier():一个创建可返回易变结果容器的函数。
  • BiConsumer<A, T> accumulator():一个将元素折叠到易变结果容器的函数。累积函数
  • BinaryOperator<A> combiner():接收两个局部结果,将它们归并到一起的整合函数。
  • Function<A, R> finisher():将中间类型 A 转化为最终结果类型 B。也就是对累积数据进行队中的转换。
  • Set<Characteristics> characteristics():返回一个 Collector.Characteristics 集合指示该 Collector 的特征,这个集合应当是不可变的。集合中的元素为:CONCURRENT 意味着支持来自多个线程的对同一结果容器并发地调用累积函数;UNORDERED 指示该集合操作不致力于保留输入元素地顺序;IDENTITY_FINISH 如果设置该属性,那么必须保证 finisher() 函数在未经检查的情况下进行对象的转化是成功的。

Collector 需要满足:

  • 同一律

    Combiner.apply(acc, []) === acc;
  • 结合律

    acc.apply(t1); acc.apply(t2) === acc.apply(t2);acc.apply(t1);
    finisher.apply(acc) === finisher.apply(combiner.apply(t1, t2))

Collectors.groupBy()

  • groupingBy(Function)
// 单纯分 key 存放成 Map<key, List>; 默认使用 HashMap
public static <T, K> Collector<T, ?, Map<K, List<T>>>
groupingBy(Function<? super T, ? extends K> classifier) // classifier 为分类的映射函数,将输入参数转变为对应的 key
  • roupingBy(Function, Collector)

    // 对输入的元素进行 group by 操作,该分类按照 classifier 转换函数将传入的元素进行转换为对应的 key,然后按照 downstrem 继续进行 reduction 操作。
    public static <T, K, A, D> Collector<T, ?, Map<K, D>> groupingBy(Function<? super T, ? extends K> classifier, // 分类转换函数 classifier
                                          Collector<? super T, A, D> downstream) { // 完成分类之后继续进行的下一步 reduction 操作
        return groupingBy(classifier, HashMap::new, downstream);
    }
  • groupingBy(Function, Suppiler, Collector)

    // 与上文 groupingBy 一致,通过 mapFactory 提供的 Map 存储结果。
    public static <T, K, D, A, M extends Map<K, D>>
    Collector<T, ?, M> groupingBy(Function<? super T, ? extends K> classifier,
                                      Supplier<M> mapFactory,
                                      Collector<? super T, A, D> downstream) 

Function Composition(复合函数)

  • f3 = f1.andThen(f2) —> f3 = f2(f1)
  • f3 = f1.compose(f2) —> f3 = f1(f2)

compositionFP.png

Optional

​ 一个容器对象可能包含或者不包含值,如果这个容器对象包含有值,那么isPresent() 方法将会返回 true 并且 get() 方法将会返回这个值。

​ 一些额外的方法依赖于容器中的值是否存在,如 orElse() 方法在容器中不存在值的情况下返回一个默认值,ifPresent() 执行一个代码块如果值存在的话。

​ 这是一个基于值的类,一些 identity-sensitive 操作,如 ==synchronization等在 Optional 实例上 将会导致不可预期的结果,应当避免使用它。

主要的方法

  • orElse(T other) :如果 ifPresent()true,返回容器内的结果,否则返回 other

  • orElseGet(Supplier<? extends T> other) : 如果 ifPresent()true,返回容器内的结果,否则返回 other 提供函数的提供对象。

  • ifPresent(Consumer<? super T> consumer):如果 isPresent()true,调用预期的 consumer 消费函数使用 value 作为参数,否则什么都不做。

  • map() (核心)处理工程中为元素不存在时依旧会继续处理,而不会抛出异常

    image-20210606152745126.png

  • flatMap(Function<? super T, Optional<U>> mapper) 不会包装转换过程中存在的 Optional 包装。