设为首页 加入收藏

TOP

Java8 函数式编程探秘(六)
2018-01-30 12:42:56 】 浏览:919
Tags:Java8 函数 编程 探秘
atic class Head<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> abstract static class StatelessOp<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT> abstract static class StatefulOp<E_IN, E_OUT> extends ReferencePipeline<E_IN, E_OUT>

至此,我们对整个流计算过程有了更清晰的认识。 细节可以再逐步推敲。

函数式编程的益处

更精练的代码

函数编程的一大益处,是用更精练的代码表达常用数据处理模式。函数接口能够轻易地实现模板方法模式,只要将不确定的业务逻辑抽象成函数接口,然后传入不同的lambda表达式即可。博文“精练代码:一次Java函数式编程的重构之旅” 展示了如何使用函数式编程来重构常见代码,萃取更多可复用的代码模式。

这里给出一个列表分组的例子。实际应用常常需要将一个列表 List[T] 转换为一个 Map[K, List[T]] , 其中 K 是通过某个函数来实现的。 看下面一段代码:

public static Map<String, List<OneRecord>> buildRecordMap(List<OneRecord> records, List<String> colKeys) {
    Map<String, List<OneRecord>> recordMap = new HashMap<>();
    records.forEach(
        record -> {
          String recordKey = buildRecordKey(record.getFieldValues(), colKeys);
          if (recordMap.get(recordKey) == null) {
            recordMap.put(recordKey, new ArrayList<OneRecord>());
          }
          recordMap.get(recordKey).add(record);
    });
    return recordMap;
  }

可以使用 Collectors.groupingby 来简洁地实现:

public static Map<String, List<OneRecord>> buildRecordMapBrief(List<OneRecord> records, List<String> colKeys) {
    return records.stream().collect(Collectors.groupingBy(
        record -> buildRecordKey(record.getFieldValues(), colKeys)
    ));
  }

很多常用数据处理算法,都可以使用函数式编程的流式计算简洁表达。

更通用的代码

使用函数接口,结合泛型,很容易用精练的代码,写出非常通用的工具方法。 实际应用中,常常会有这样的需求: 有两个对象列表srcList和destList,两个对象类型的某个字段K具有相同的值;需要根据这个相同的值合并对应的两个对象的信息。

这里给出了一个列表合并函数,可以将一个对象列表合并到指定的对象列表中。实现是: 先将待合并的列表srcList根据key值函数keyFunc构建起srcMap,然后遍历dest列表的对象R,将待合并的信息srcMap[key]及T通过合并函数mergeFunc生成的新对象R添加到最终结果列表。

  public static <K,R> List<R> mergeList(List<R> srcList, List<R> destList ,
                                        Function<R,K> keyFunc,
                                        BinaryOperator<R> mergeFunc) {
    return mergeList(srcList, destList, keyFunc, keyFunc, mergeFunc);
  }

  public static <T,S,K,R> List<R> mergeList(List<S> srcList, List<T> destList ,
                                            Function<S,K> skeyFunc, Function<T,K> dkeyFunc,
                                            BiFunction<S,T,R> mergeFunc) {

    Map<K,S> srcMap = srcList.stream().collect(Collectors.toMap(skeyFunc, s -> s, (k1,k2) -> k1));
    return destList.stream().map(
        dest -> {
          K key = dkeyFunc.apply(dest);
          S src = srcMap.get(key);
          return mergeFunc.apply(src, dest);
        }
    ).collect(Collectors.toList());

  }

更可测的代码

使用函数接口可以方便地隔离外部依赖,使得类和对象的方法更纯粹、更具可测性。博文“使用Java函数接口及lambda表达式隔离和模拟外部依赖更容易滴单测”,“改善代码可测性的若干技巧”集中讨论了如何使用函数接口提升代码的可单测性。

组合的力量

函数编程的强大威力,在于将函数接口组合起来,构建更强大更具有通用性的实用工具方法。超越类型,超越操作与数据的边界。

前面提到,函数接口就是数据转换器。比如Function<T,R> 就是“将T对象转换成R对象的行为或数据转换器”。对于实际工程应用的普通级函数编程足够了。不过,要玩转函数接口,就要升级下认识。 比如 Function<BiFunction<S,Q,R>, Function<T,R>> 该怎么理解呢?这是“一个一元函数g(h(s,q)) ,参数指定的二元函数h(s,q)应用于指定的两个参数S,Q,得到一个一元函数f(t),这个函数接收一个T对象,返回一个R对象”。 如下代码所示:

  public static <T,S,Q,R> Function<BiFunction<S,Q,R>, Function<T,R>> op(Function<T,S> funcx, Function<T,Q> funcy) {
    return opFunc -> aT -> opFunc.apply(funcx.apply(aT), funcy.apply(aT));
  }

  System.out.println(op(x-> x.toString().length(), y-> y+",world"
首页 上一页 3 4 5 6 7 8 9 下一页 尾页 6/9/9
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇通向架构师的道路(第六天)之漫.. 下一篇JUnit 源码解析

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目