面向对象的编程-Application 32

Previously on OOP:

toSet() collector can eliminate duplicated elements. A Lambda expression can be used as a predicate of filter() and map() methods. The concept of flatMap() method is to extract a column composed of Collections and re-organize them into a new Stream.

map()函数只能把Stream转化为Stream类型,但是用mapToInt()函数可以转化为Stream类型。Stream里面的数据类型是wrapper class还是primitive type,对于max()函数是有区别的。如果是primitive type,max()函数的predicate可以为空;反之则要在predicate中指明两个Stream的实例的比较大小的方法,如下所示:

这个Lambda expression应该是Comparator functional Interface的子类的实例,重载的是compareTo()函数。另外,定义某类的实例之间的大小的常用方法有:

(1)implements Comparable Interface:这个类不是一个functional Interface,也就是里面包含不止一个需要重载的函数;或者需要重载的函数不仅仅和传入的参数有关,还和实例本身有关。Comparable Interface的情况属于后者。

(2)implements Comparator Interface:这是一个functional Interface,所以适合用Lambda expression来编写。

(3)调用comparing()函数:这个函数在Comparator Interface中。因为是一个静态函数,所以在implements Comparator Interface的时候不需要重载它。

最后,max()函数的返回值应该是Optional类型的,即可能没有返回值。本黄鸭不得不再重申一遍“没有返回值”和“返回值是void”的区别,像constructor那样的叫做没有返回值。对于Optional类型,我们还可以调用.isPresent(), .ifPresent(), get(), orElse(),等等函数来分类讨论。本段代码中的orElse()函数表示在max()函数没有返回值的时候,把值设为。

上一个requirement是要比较学生姓氏的长度,现在是要比较学生实例之间的大小,比较的标准是他们姓氏的长度,姓氏越长就越大。所以,本段代码的重点是把Student objects之间大小关系作为max()函数的predicate。用刚才讲过的三种方法都可以实现,现在本黄鸭决定选用comparing()。

comparing()函数的参数是作为比较标准的那个attribute。而attributes一般都是private的,想要取得它们的值只能调用getters。“s”是一个Student类型的临时变量,先调用getLast()函数取得学生的姓氏,顺便把类型转化为String。再调用String类中的length()函数取得学生姓氏的长度,即我们的比较标准。

此外,在本例中,千万不能调用map()或者mapToInt()函数,否则Stream中没有办法包含Student objects。类似地,本段代码中的max()函数的返回值也是Optional,可以继续分类讨论。

有的宝宝为了避开max()函数的Optional类型的返回值,喜欢调用“.sorted().limit(1)”。这种方法是可以实现需求的,但是比起max()来说复杂度提升了很多。所以,在没有明确要求排序的时候,本黄鸭不建议调用sorted()函数。不过这个需求是明确要求排序的,所以非常合适调用sort()函数。

需求中还有一个重点在于“separated by ","”。有的宝宝觉得使用非Stream的方法很好实现:

(1)编写一个for / for each循环,然后在一个StringBuffer的后面不断地append学生姓氏和逗号。顶多再判定一个学生姓氏是不是最后一个,如果是,那么就少append一个逗号即可。

(2)把学生姓氏存放在一个Collection类型的数据结构中,然后调用toString()函数。在运气好的情况下,系统默认的分隔符就是逗号。

既然我们在举Stream的例子,本黄鸭还是要用Stream的方法来实现的。在predefined accumulating collector中,有一个叫做joining()的小众collector,功能是concatenates elements into a new String,参数是separator。那么,我们只要调用joining() collector就可以了。

groupingBy()是一个常用的predefined grouping collector,功能是把Stream整合成一个Map,接收的第一个参数是作为Map的key field的attribute。同样地,attributes一般都是private的,访问只能通过调用getters,所以,groupingBy()的第一个参数会是method reference或者Lambda expression。在本段代码中,Map的key field是学生的性别。

如果groupingBy()没有接收到其他参数,就把key对应的Stream中的数据作为Map的value field。假设还有一个key对应多个数据的情况,那么这个collector会自动把多个数据合并为一个List,存为value field。在本段代码中,除了“Student::getGender”之外没有参数,所以value field应该是Student的实例,或者Student的实例构成的List,用代码来写就是“List”。

本段代码的groupingBy()有了第二个参数,counting()。它本身也是一个predefined collector,功能是统计数据的个数。现在,counting()作为参数,指定了生成Map的value field不是List,而是List再做counting(),即对应Gender的学生的人数。

还有一点需要注意,counting() collector的返回值是Long,不是Integer。这样才使得返回值的类型是:Map。

本黄鸭刚刚才说过,当groupingBy()只有一个参数时,参数指定的是一个attribute,作为生成Map的key field。这个attribute不仅可以用method reference来写,还可以用Lambda expression。本段代码中就采用了后者。

“s”是一个Student类型的临时变量,调用了Student类的enrolledIn()函数,取得courses attribute,即这名学生参加的所有课程的Collection。此时类型变为Collection。接着调用Collection类的size()函数,求这名学生参加所有课程的数量,并作为生成Map的key field。

又因为返回值类型是“Map>”,所以:

(1)要把key field强制转换为“Long”类型

(2)groupingBy()不需要其他参数

在理论部分,本黄鸭还提到过一种接收三个参数的groupingBy() collector,多出来的参数一般是“TreeMap::new”,位置在第二个。这种groupingBy()生成的Map,会按照key field的值排序。

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

微信公众号二维码:

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

扫码关注云+社区

领取腾讯云代金券