继续看《java 8 函数式编程》,思考一些传达的设计思路吧。我们在调试时通常会设置断点,单步跟踪程序的每一步。使用流时,调试可能会变得更加复杂,因为迭代已交由类库控制,而且很多流操作是惰性求值的。
然后先说一下java8由外部迭代到内部迭代,java程序员在使用集合类时,我们长做的事情就是在集合上进行迭代,然后处理返回的每一个元素。然后作者可能觉得每次迭代,需要写不少样本代码,并且将for循环改造成并行方式也很麻烦。for循环其实算是一个封装了迭代的语法糖,一般首先调用 iterator 方法,产生一个新的 Iterator 对象,进而控制整 个迭代过程,这就是外部迭代,书中给的图也不错。
这从本质上来讲是一种串行化操作,总体来看,使用 for 循环会将行为和方法混为一谈。另一种方法就是内部迭代,首先要注意 stream() 方法的调用,该方法不是返回一个控制迭代的 Iterator 对象,而是返 回内部迭代中的相应接口:Stream。
由于Stream API的函数式编程风格,我们并没有改变集合的内容,而是描述出Stream里的内容。判断一个操作是惰性求值还是及早求值很简单:只需看它的返回值。如果返回值是 Stream, 那么是惰性求值;如果返回值是另一个值或为空,那么就是及早求值。使用这些操作的理想方式就是形成一个惰性求值的链,最后用一个及早求值的操作返回想要的结果,这正是它的合理之处。整个过程和建造者模式有共通之处。建造者模式使用一系列操作设置属性和配置,最后调用一个 build 方法,这时,对象才被真正创建。
调试时通常会设置断点,单步跟踪程序的每一步,但是我们经常在集合上遍历,然后操作数据,你是要测试代码,希望看到每一步的操作结果是什么,在每一步中打印出集合的值,这个在for循环中是很容易做到的,但是现在事惰性求值的流中,这就比较复杂了。
当然在流中 有forEach操作,但是这样我们就无法操作流了,可以看到forEach返回的结果是void,如果我们还要使用流,那必须要重新创建一遍,好尴尬的代码呀。
但是java的设计者都是大神呀,给我们提供了解决方案,那就是使用peak,可以让我们查看每一个值,同时能继续操作流,这就是我们的peak方法了。
当然,我们都在应用中使用日志系统(eg..log4j,slf4j),据说peek也可以用同样的方式,将输出定向到日志系统中,下次试试看。毕竟查bug看栈信息 ,更直接的是看日志。peek是个中间操作,它提供了一种对流中所有元素操作的方法,而不会把这个流消费掉。
但是如果不写后面的collect求值操作,发现peek什么都不输出,流遇到及早求值的时候,才会真正去遍历和执行,还要写在求值操作前,有点奇葩,因为peek操作返回的还是一个流,当然peek可以写在filter后 map后多次写是没问题的。
这么说peek到底怎么运作的
你觉得输出的日志会是什么?
微信公众号: