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


在应用map()期间组合流
flatMap(),其实和map()的区别就是,有时候我们提供的参数方法会返回一个流而不是一个元素 。这样的话,我们就需要另一个方法能够以流为参数进行处理,也就是需要一个方法把我们返回的流平展开成为元素,类似于把所有返回的流拼接在一起,成为一个更大的流然后再进行处理 。
一个典型的例子:
public static void main(String[] args){Stream.of(1, 2, 3).flatMap(i -> Stream.of('a', 'b', 'c')).forEach(System.out::println);// 上面的flatMap()处如果使用map()那么会返回三个元素为{a, b, c}的流// 而如果是faltMap()则返回的是元素为{a, b, c, a, b, c, a, b, c}的流}3、Optional类型到此我们已经了解了流的创建和中间操作,但是在学习终结操作之前,我们还有一个更重要的问题:健壮性研究 。
在前面的处理环节我们需要考虑,如果流中存在一个null会发生什么呢?要知道流可不是什么快乐通道,作为程序员,我们必须要考虑周全,环环相扣 。
所以为了防止在某些不该出现null的地方出现了null导致处理失败 , 我们需要一个类似占位符的存在 , 它既可以作为流元素占位也可以在我们要找的元素不存在时告知我们(即不会抛出异常)
这个想法的实现就是Optional类型,这些类型只会通过某些标准流操作返回,因为这些操作不一定能保证所要的结果一定存在:

  • findFirst()返回包含第一个元素的Optional,若流为空 , 则返回Optional.empty
  • findAny()返回包含任何元素的Optional , 若流为空 , 则返回Optional.empty
  • max()min()分别返回包含流中最大或最小值的Optional,若流为空,则返回Optional.empty
  • reduce()的其中一个实现,参数为一个接收两个参数并返回一个结果的方法引用,其作用就是返回各个元素根据该参数计算得到的值,其中每次迭代计算出的值会作为下一次计算的第一个参数比如1,2,3,4给出reduce((x1, x2) -> x1+x2)那么计算流程会是1+2=3, 3+3=6,6+4=10
  • average()可以对数值化的流计算均值并以对应的Optional类对象返回
现在,我们可以从流中获取Optional对象了,那么有什么用呢?这就要提到便捷函数了
便捷函数可以用于获取Optional中封装的数据 , 并且简化了步骤
  • ifPresent(Consumer):如果值存在,则通过该值调用Consumer函数 , 否则跳过
  • orElse(otherObject):如果值存在,则返回该对象,否则返回参数对象
  • orElseGet(Supplier):如果值存在 , 则返回该对象,否则返回Supplier方法创造的对象
  • orElseThrow(Supplier):如果值存在,则返回该对象,否则抛出一个使用Supplier方法创造的异常
如果我们需要自己创建Optional对象 , 那么我们可以使用这些Optional类的静态方法:
  • empty():返回一个空的Optional
  • of(value):如果已经知道这个value不是null,可以使用该方法把它封装在一个Optional对象中
  • ofNullable(value):如果不能确定封装值是不是null,则使用此方法封装
最后,还有三种方法支持对Optional进行事后处理 , 提供最后一次处理机会
  • filter(Predicate)
  • map(Function)
  • flatMap(Function)
它们的作用都和中间操作中的对应方法一致,只不过返回值会被封装在Optional对象中
最后,回到我们的主角Stream上来,有时候,我们不是给出的参数含有null而是处理的结果可能含有null那么我们可能会希望将这些返回值包含在Optional对象中,那么我们可以通过类似x -> Optional.of(result)这样的方法将其封装,但是 , 如果这么做了就一定要清楚我们该如何获取这样的流中的对象 。请牢记,要先验证是否存在,才能获取
Stream.filter(Optional::isPresent).map(Optional::get)// 到这里,流中的数据就都是Optional对象中包含的值了// 继续处理4、终结操作这些操作接受一个流作为参数,并生成一个最终结果而非返回那个流 , 因此,只要调用这些方法,流处理就将终结

推荐阅读