面向对象的编程-Application 45

Previously on OOP:

University project can be implemented using Java Generics.

其次,本黄鸭还增加了一道附加题:在University类的、存放所有学生信息的include Map中,要求按照学生学号排序。实现这个需求,只要把include的object的类型从HashMap改成TreeMap就行了,不需要在Student类中添加implements Comparable Interface。如果是把HashMap改成TreeList / TreeSet,才有Comparable出场的机会。

listAttendees()函数的返回值是参加某课程的、所有学生的信息。为了进一步地提高难度系数,本黄鸭决定再追加一项需求:学生要按照学号排序。

University类中的listAttendees()函数一切照旧。先用课程编号从存放所有课程信息的Map中取出对应课程的object reference,再用该object reference调用Course类中的attendees()函数。

Furthermore, the implementation to get the study plan for a single student is similar to listAttendees() method.

在没有追加需求的情况下,attendees()函数的编写如下:

先创建一个空的StringBuffer,然后用循环结构把一条条Student信息转化为字符串,append在StringBuffer的后面,最后把StringBuffer转化为String,作为返回值返回。

当然,创建的是一个空的String,再使用加号把各种字符串concatenate在一起,也是完全可以的。

另外,想要实现追加的需求,我们可以有以下几种想法:

(1)一边concatenate String / append StringBuffer,一边排序。

请参考一下选择排序,再把这种想法转化为代码。假设一共有N个学生参加这门课程,会有一个循环遍历N遍存放所有学生的数据结构。第一次遍历的任务是找到学号最小的学生,第二次是找到比第一次大且学号最小的学生,以此类推,直到找不到比上次的学号还要大的学生,也就是所有学生都被放到String / StringBuffer中了。

以上算法的复杂度也和选择排序相同,是O(N^2),属于时间复杂度相对比较高的。

(2)在concatenate String / append StringBuffer之后,再排序。然而,目前本黄鸭没有想到用哪一种算法来实现这个想法,所以,请各位宝宝们果断放弃。

(3)在concatenate String / append StringBuffer之前,先排序。本黄鸭觉得这种是最容易实现的,因为总共有五种方法可以实现。

Solution 1

调用Collections类中的sort()函数来排序。因为dotted notation前面是类的名称,而不是object reference,所以我们有理由相信sort()函数是一个static的函数。

排序的前提是比较,所以我们需要定义两个Student类型的objects之间的大小关系。又因为Course类中没有比较Student objects大小的代码,只能到Student类中去寻找,所以本段代码必须和Student implements Comparable Interface一起使用。先在Student类的声明行加上implements Comparable ,再override compareTo()函数。

Solution 2

本段代码的Student类不需要implements Comparable Interface,因为这里已经选用了Comparator Interface。

虽然Comparable和Comparator都是Interfaces,而且里面都只有一个叫做compareTo()的method需要被子类overridden,但是只有Comparator是functional Interface,这是因为Comparable的compareTo()和当前object有关,而Comparator的compareTo()和当前object无关。

既然只有Comparator是functional Interface,那么也只有它能使用Lambda expression。然而,本段代码中只用到了functional Interface的性质,没有使用Lambda expression。

Collections.sort()函数收到的第一个参数是students,即待排序的数据结构。第二个参数是一个anonymous inner class。

本黄鸭曾经说过,一个实例的类型不能直接是某个Interface类型的,必须要先创建该Interface的子类,才能创建实例。所以第二个参数不能理解为调用构造函数,而是anonymous inner class。

Anonymous,翻译为匿名,顾名思义,在类的声明部分没有Comparator Interface的子类的名称,只有关键字“new” + Comparator类型。

在类的声明后面有一对花括号,里面写的是这个类的内容,只有一个重载的compareTo()函数。如果把这个函数的返回值改成相反数,会从sort in ascending order变为sort in descending order。

Solution 3

现在是Comparator functional Interface的Lambda expression的写法。s1和s2是两个Student类型的临时变量。

Solution 4

前面三种方法分别是实现比较的两种方法:Comparable Interface,以及Comparator Interface。而本段代码使用的是第三种方法:Comparator Interface中的一个comparing() static method。这个函数的需要的参数是key extractor,即用来作为比较标准的那个attribute,也就是学生的学号。

comparing()函数不需要我们从零开始自己编写一个,只要会调用就可以了。有兴趣的宝宝们可以稍微看一下comparing()的函数体:

Solution 5

最后一种方法和Solution 4一样,都用的是comparing() static method。区别在于作为参数的key extractor没有用Lambda expression来写,而采用了method reference。

本黄鸭不得不再强调一遍Lambda expression表示调用和method reference之间的区别:

(1)写法不同。

Lambda expression的左边是parameters,即可以指明类型,也可以不指明的临时object references。中间用“->”连接。右边是expression,即使用object references调用的函数。

Method reference的左边是类的名称。中间用“::”连接。右边是方法的名称,没有括号。

(2)能连续调用函数的个数不同。

Lambda expression右边的表达式可以包含cascaded, combined, and multiple dotted notations。而Method reference右边只能有一个method name。

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

微信公众号二维码:

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

扫码关注云+社区

领取腾讯云代金券