首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

面向对象的编程-Application 19

Previously on OOP:

HashMap is one kind of associated containers. Key fields and value fields must be stored in pair in this kind of containers. Furthermore, we had a discussion on“type invariant”.

Stream是一种Java中特有且高效地批量处理数据的方法。使用Stream能解决的问题,在原则上,都可以使用Generics来解决,尽管效率非常低,编码各种纠结。在本课程中,Stream是必须必要点亮的一项技能,因为考试中一定会有它的题目。在本文中,本黄鸭会举一些Stream的例子。

Stream的source可以有几种:数组,Generics,a sequence of objects of the same type。本黄鸭先采用第一种,数组。

有一个字符串叫做test,内容如上段代码。调用split()函数,能把test字符串按照空格分割成各个小字符串,并且存放在数组words里面。此时的words可以作为一个合格的Stream source。

调用Arrays类的stream()函数,能把words字符串转化为Stream source。It is an educated guess thatstream()函数是一个static method,因为在dotted notation前面不是object reference,而是类的名称。

接下来,调用sorted()函数把字符串按照字母的先后顺序排序。第二个educated guess是sorted()函数的返回值应该是一个Stream类型的object reference,否则没有办法把结果视为一个object reference,继续写dotted notation来调用洽谈Stream的函数。

limit(3)和sort()函数一样,都是Stream处理的中间步骤,功能是取前三个字符串。在理论部分我们讨论过,如果有两个字符串并列第三名的时候,这个方法会踢掉任意一个获得第三名的字符串。而为了避免被踢掉,可以使用“.entrySet().stream()”的做法,把所有并列名次的字符串打包在一起。这里我们先不演示entrySet()。

此外,还有一个问题:为什么是按照“字母的先后顺序排列”呢?为什么不是按照字符串的长短呢?这个问题很好,但是要回答必须想起大明湖畔的Comparable, Comparable, comparing这三位大侠,它们被用来定义两个相同class的objects之间的大小关系。有了这三位大侠,想怎么定义字符串之间的大小都可以实现。

最后,termination process of Stream不是任何一个函数都能充当的,必须在规定的类别里面,详情请参考理论部分。forEach()就是其中的一个,它接受的参数是一个method reference,即一种调用。被调用的函数是println()。所以,打印的结果就是排名前三的三个字符串。

有的宝宝会认为Lambda expression and method reference有千丝万缕的联系,因为这两个都完全不会。当然,这种认知是完全错误的,结合迄今为止的例子,我们来梳理一下:

在其他章节,Lambda expression表示的是Interface的子类的实例,因为Interface是一种特殊的类,不能直接创建实例。而在Stream章节,不管是Lambda expression还是method reference,都可以看做为调用,和dotted notation差不多。所以,且不论其他章节,Stream章节的这两种表达式是有机会抢救的。

从写法上来看,Lambda expression的写法包含“->”符号,左边是parameter,右边是表达式。而method reference的写法如下:

比如,本段代码的左边的“System.out”是一个fully qualified name of a class,右边的“println”是一个函数的名称。

既然Lambda expression and method reference同为调用,两者之间有什么区别呢?前者的右边部分是一个表达式,在表达式中可以结合dotted notation,连续调用好几个方法。而后者的右边只能有一个方法的名称。

用Generics的方法来实现上段代码的功能也是可以的,有兴趣的宝宝们可以自己试一试。

虽然上面那段代码比起Generics的方法已经简洁了很多,但是不排除还有进一步优化的空间。排序是一种非线性的操作,连最好的recursive的算法,只能把复杂度降低到log(n),n是要排序的元素的个数,趋近于无穷大。也就是说要排序的元素的个数越少,那么排序的运算速度就越快。所以,我们可以在排序之前先踢掉一些元素。

现在假设长度小于3的字符串全是以x,y,z字母开头的,就算是加入排序,也不可能排到前三名,所以可以用filter()函数把这些在排序之前全踢出去。

filter()函数的参数是一个Lambda expression。s是一个临时变量,编译器能自动地理解为Stream中存放的数据的类型,也就是String类型;所以把临时变量命名为“s”特别合适,有助于提醒开发者变量的类型。

“->”的右边是表达式,调用String类的length()函数,并且判定字符串的长度是否大于3。如果大于3,那么就保留,进入下一环节;反之,则踢掉。

像这样的表达式是不能改成method reference的,只能用Lambda expression,这是因为不仅仅只调用了length()这一个函数,还和3进行了比较。

除了用filter()函数踢掉几行数据之外,还能用map()函数踢掉几列。在本段代码中,map把字符串的各种信息(字符串的内容,是否全大写,等等)全部都踢掉了,只保留字符串的长度信息。

map()函数接收到的参数是一个method reference,因为这里只调用了唯一的一个函数,就是length()。在map()函数执行之后,原来Stream中的元素都是String类型的,现在都变成了Integer类型(wrapper class)。也就是说,万一后面再有Lambda expression的调用,临时变量的名字最好叫做“i”。

如果想要map()函数执行之后,Stream的数据都是int类型(primitive type),应该怎么做呢?答案是把map()函数换成mapToInt()函数。

本段代码中还提出了一个非常有价值的问题:调用sort()函数能正序排列,那么如何sort in reverse order呢?欲知后事如何,且听下回分解。

欢迎使用本黄鸭编写的小程序~

微信公众号二维码:

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20181019G1J70U00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券