• Java 函数式编程「一」


    由 JS 转 Java,写惯了 React,习惯了函数式,因此转 Java 时也是先学函数式。

    语法糖「Syntactic Sugar」

    起初,Java 的函数式看起来是匿名类的一个语法糖。

    Stream.of(1, 2, 3).filter(new Predicate() {
      @Override
      public boolean test(Integer integer) {
        return integer % 2 == 0;
      }
    }).collect(Collectors.toList());
    
    Stream.of(1, 2, 3).filter(x -> x % 2 == 0).collect(Collectors.toList());
    

    后来,看到也能传方法引用时,我陷入了怀疑。

    // 类名::static 方法
    Stream.of(4, 2, 3).sorted(Integer::compare);
    
    // 对象::对象方法
    Stream.of(1, 2, 3).forEach(System.out::println);
    
    // 类名::对象方法
    Stream.of(4, 2, 3).sorted(Integer::compareTo);
    

    不过再想想,还是可以理解成语法糖:

    Stream.of(1, 2, 3).forEach(System.out::println);
    
    Stream.of(1, 2, 3).forEach(new Consumer() {
      @Override
      public void accept(Integer integer) {
        System.out.println(integer);
      }
    });
    

    闭包「Closure」

    Java 闭包也是假的,和匿名类的限制一样,要求闭包访问的外部作用域变量是 final 的。实现应该都是:基础类型值传递,引用类型引用传递。

    以下 Java 代码编译会报错:

    int index = 0;
    Stream.of("a", "b", "c")
      .forEach(x -> System.out.println((index++) + ":" + x));
    

    Error: Variable used in lambda expression should be final or effectively final

    JS 闭包毫无问题:

    let index = 0;
    ["a", "b", "c"].forEach(x => console.log(index++ + ":" + x));
    

    尽管如此,函数式习惯还是可以带到 Java 了。

    第一个要带过来的是:少量的数据结构搭配大量的操作。

    在 OOP 的世界里,开发者被鼓励针对具体的问题建立专门的数据结构,并以方法的形式,将专门的操作关联在数据结构上。函数式编程语言选择了另一种重用思路。它们用很少的一组关键数据结构( 如 list、 set、 map)来搭配专为这些数据结构深度优化过的操作。我们在这些关键数据结构和操作组成的一套运转机构上面,按需要“ 插入” 另外的数据结构和高阶函数来调整机器,以适应具体的问题。

    Neal Ford. 函数式编程思维

    在 Java 中,关键数据结构就是指 Stream

    Stream 操作三板斧:mapfilterreduce

    Java 和 JS 有些不一样。

    不同点一:index 的取法

    JS mapfilterreduce 都能拿到 index

    ["a", "b", "c"]
      .map((s, index) => `${index}: ${s}`)
      .forEach(s => console.log(s));
    

    Java 想要 index 的信息,对于数组类的,可以构造并遍历 indexstream

    String[] arr = new String[]{"a", "b", "c"};
    IntStream.range(0, arr.length)
      .mapToObj(index -> StringFormatter.format("%d: %s", index, arr[index]).get())
      .forEach(System.out::println);
    

    也可以先通过 zip 让流中的数据带上 index

    public static  List> zip(List s1, List s2) {
      return IntStream.range(0, Math.min(s1.size(), s2.size()))
        .mapToObj(index -> new Pair(s1.get(index), s2.get(index)))
        .collect(Collectors.toList());
    }
    
    public static void main(String[] args) {
      List list = Arrays.asList("a", "b", "c");
      zip(IntStream.range(0, list.size()).boxed().collect(Collectors.toList()), list)
        .parallelStream()
        .map(p -> p.getKey() + ":" + p.getValue())
        .forEach(System.out::println);
    }
    

    不同点二:reduce 的用法

    Java 分为了 reductionmutable reduction,在 JS 里是不区分的。

    A mutable reduction operation accumulates input elements into a mutable result container, such as a Collection or StringBuilder, as it processes the elements in the stream.

    • JS reduction:

      [1, 2, 3].reduce((acc, cur) => acc + cur, 0);
      
    • Java reduction:

      Stream.of(1, 2, 3).reduce(0, Integer::sum);
      
    • JS mutable reduction:

      [1, 2, 2].reduce((acc, cur) => {
        acc.add(cur);
        return acc;
      }, new Set());
      
    • Java mutable reduction:

      Stream.of(1, 2, 2).collect(
        () -> new HashSet<>(),
        (set, el) -> set.add(el),
        (s1, s2) -> s1.addAll(s2)
      );
      

      也可以简写为:

      Stream.of(1, 2, 2).collect(
        HashSet::new,
        HashSet::add,
        HashSet::addAll
      );
      

      参数比 JS 多了个 HashSet::addAll 是为了并行处理。


    后续将总结函子、高阶函数在 Java 中的应用。

  • 相关阅读:
    URL转发请求
    一个ubuntu系统搭建redis集群
    vue3 hook库
    【无标题】 python 数据写入excel
    【计算机网络】https协议
    SpringBoot第 15 讲:SpringSecurity
    vue之Error: Unknown option: .devServer.
    Dubbo常考知识点
    索引的数据结构、索引及其优缺点、索引的声明与使用以及索引的设计原则
    Linux之线程概念
  • 原文地址:https://www.cnblogs.com/apolis/p/16688574.html