写点什么

Java 函数式编程

作者:Java-fenn
  • 2022 年 9 月 14 日
    湖南
  • 本文字数:1792 字

    阅读完需:约 6 分钟

语法糖「Syntactic Sugar」

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

Stream.of(1, 2, 3).filter(new Predicate<Integer>() {  @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::compareTo);
复制代码


// 对象::对象方法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<Integer>() { @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 操作三板斧: map 、 filter 、 reduce

Java 和 JS 有些不一样。

不同点一: index 的取法

JS map 、 filter 、 reduce 都能拿到 index :

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

Java 想要 index 的信息,如果只是单线程,可以考虑利用引用类型保存迭代的索引:

AtomicInteger i = new AtomicInteger(0);Stream.of("a", "b", "c")  .map(x -> i.getAndIncrement() + ":" + x)  .forEach(System.out::println);
复制代码

如果想支持多线程,可以先通过 zip 让流中的数据带上 index :

public static <T, R> List<Pair<T, R>> zip(List<T> s1, List<R> s2) {  return IntStream.range(0, Math.min(s1.size(), s2.size()))    .mapToObj(index -> new Pair<T, R>(s1.get(index), s2.get(index)))    .collect(Collectors.toList());}
public static void main(String[] args) { List<String> 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 分为了 reduction 和 mutable 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 中的应用

用户头像

Java-fenn

关注

需要Java资料或者咨询可加我v : Jimbye 2022.08.16 加入

还未添加个人简介

评论

发布
暂无评论
Java 函数式编程_Java_Java-fenn_InfoQ写作社区