Java函数式编程:三、流与函数式编程

本文是Java函数式编程的最后一篇,承接上文:Java函数式编程:一、函数式接口,lambda表达式和方法引用Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化
前面都是概念和铺垫,主要讲述了函数式编程中,如何获取我们需要的函数作为参数或输出来进行编程,同时补充了一些要注意的知识 。比如柯里化,闭包等等 。
而这一篇要讲的是Java函数式编程的主菜,也就是如何把我们苦苦获取的函数 , 运用在真正的对于数据的处理之中 。
在以前,我们通常会通过一个集合把这些数据放在一起,然后详细编写其处理过程使之能被逐一处理,最后再通过一个集合把它们获取出来 , 这没有任何问题 。但是对于某些情况下而言,我们已完全洞悉并且厌烦了这些处理过程,我们渴望获得一种更轻便,更简易的手段,能使得整个集合中的数据处理就像水流通过管道一样,我们可以随意在这条管道上拼接各式各样的制式的处理器来处理这些数据,并最后给出一个结果 。——这个制式的处理器就是我们的函数 , 而这个管道就是流
流流是一个与任何特定的存储机制都没有关系的元素序列,我们一般会这样说流:没有存储 。
不同于对于任何一个集合的操作,当我们使用流时,我们是从一个管道中抽取元素进行处理 , 这非常重要,因为大多数时候我们不会无缘无故的将元素放进一个集合,我们一定是希望对其进行一些处理 , 也就是说,我们不是为了存储才将它们放入集合的 。
如果是这样,那么就意味着我们的编程很多时候需要转向流而不是集合 。
流最关键的优点是 , 能够使得我们的程序更小也更好理解 。事实上,lambda函数和方法引用正是在这里才发挥出了其真正的威力,它们一同将Java带入了声明式编程:我们说明想要完成什么 , 而不是指明需要怎么去做 。

  • 类似流+函数式编程这样实现的声明式编程机制,就被称之为内部迭代,我们看不见其内部的具体操作
  • 而通过循环,将内部的数据一个一个处理成型的机制就被称为外部迭代,我们可以显式的看清和修改内部的操作
流带来的声明式编程是Java 8最重要的新特性之一,为此,Java还引入了新的关键词default以便它们大刀阔斧的修改一些老的集合类 , 以便使得它们支持流 。
下面,我们将分三个阶段来了解,我们可以怎样去使用流,并运用流和函数式编程获得极佳的编程体验
  • 流的创建
  • 流的中间操作
  • 流的终结操作
1、流的创建【Java函数式编程:三、流与函数式编程】最基本的流的创建方法就是
  • Stream.of(一组条目)
  • Collection.stream()
我们可以把任意相同类型的一组条目写在Stream.of()的参数中使之变成一个流,比如:
Stream.of("a", "b", "c", "d");Stream.of(new Node(1), new Node(2), new Node(3));Stream.of(1, 2, 3, 4, 5);Collection接口的stream()方法则更是我们的好伙伴,所有实现了该接口的集合,都可以直接转变为一个流由我们处理 。
此外,我们还有以下生成流的手段
  • 随机数流
  • int基本类型的区间范围方法
  • generate()方法
  • iterate()方法
  • 流生成器
  • Arrays.stream()将数组转换为流
  • 正则表达式
下面来逐一了解
随机数流
Random类已经得到了增强 , 现在有一组可以生成流的方法 。
  • ints()
  • longs()
  • doubles()
  • boxed()
可以清楚的看到,我们只能通过Random类获取三种基本类型的流,或者在其后加上boxed()来获取它们的包装类的流 。实际上 , Random类生成的这些数值 , 还有别的价值,比如通过随机数来获取某个列表中的随机下表对应值,以此来获取随机的对象 。
int区间范围方法
IntStraem类提供了新的range()方法,可以生成一个流,它代表一个由int值组成的序列,对于IntStream.range(a, b)来说 , 这个流中的数据是[a, b)区间的所有整数 。
利用这个方法,我们可以通过流很好的代替某些循环了,比如:
public class Repeat{public static repeat(int n, Runnable action){IntStream.range(0, n).forEach(i -> action.run());}}

推荐阅读