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

语法糖「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());

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

Stream.of(1, 2, 3).forEach(System.out::println);
StringBuilder sb = new StringBuilder();
Stream.of(1, 2, 3).forEach(sb::append);
System.out.println(sb.toString());
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 操作三板斧:mapfilterreduce

Java 和 JS 有些不一样。

不同点一:index 的取法

JS mapfilterreduce 都能拿到 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 分为了 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 中的应用。

最新文章

  1. Jmeter测试结果分析
  2. bash快捷建
  3. css 常用代码解析
  4. adb devices找不着设备
  5. HDU 2063 过山车 (最大匹配,匈牙利算法)
  6. ubuntu使用问题与解决记录[持续更新]
  7. css3学习笔记之多列
  8. 「书评」SAP内存计算——HANA
  9. 让linux(centos)支持中文文件和文件夹
  10. c语言typedef运用之结构体
  11. 将node-expat扩展编译至node.exe中
  12. nuget pack 时不包含依赖包(而不是引用项目的dll,区别于IncludeReferencedProjects)
  13. (七十五)CoreLocation(一)在iOS7和iOS8设备上获取授权
  14. [PKUWC2019]Day1 T2 你和虚树的故事
  15. spring的定时器
  16. 20175202 《Java程序设计》第三周学习总结
  17. C++回顾day01---&lt;命名空间&gt;
  18. 001.LVS简介及算法
  19. 解决pycharm在ubuntu下搜狗输入法一直固定在左下角的问题
  20. SQL Server 用角色(Role)管理数据库权限

热门文章

  1. oracle-安装与访问、卸载
  2. EasyExcel导出创建Excel下拉框
  3. 017(Power Strings二刷)(KMP)
  4. vue2.0 双向绑定原理分析及简单实现
  5. Eclipse拷贝动态的web工程
  6. Hashtable集合 --练习题_计算一个字符串中每个字符出现次数
  7. DNS 系列(三):如何免受 DNS 欺骗的侵害
  8. python解决“failed to execute pyi_rth_pkgres”问题
  9. Kafka启动遇到ERROR Exiting Kafka due to fatal exception (kafka.Kafka$)
  10. Vue生命周期和MVVM框架