一、问题的提出:
现需要实现不可变类Interval,用开始和结束时间标识一个时间间隔:
/**
* An interval defind by start and end, start <= end,
* represents the section from start to end including both start and end.
*
* Interval is immutable
*/
public class Interval {
private final long start;
private final long end;
在ADT中需要对Interval数组按开始时间排序,但Interval是不可比较的,所以要将它包装成可比较类
二、实现方法
1. 实现Comparable接口
首先查看其API说明:
由此可见,让Interval类实现Comparable的compareTo方法后就可以通过Collectioins或Arrays提供的sort方法对Interval数组排序。
而compareTo要满足API中描述的三条性质,本例中要比较的数据类型为long,故可以使用可Long类型的compareTo, 实现如下(声明implements Comparable<Interval>):
/**
* Compare by start
* @param that
* @return
*/
public int compareTo(Interval that) {
Long l = Long.valueOf(this.start);
return l.compareTo(that.getStart());
}
对Interval数组list按升序排序:
Collections.sort(list);
2. 实现Comparator接口
和Comparable类似,Comparator通过compare方法为Collections和Arrays的sort方法提供比较规则:
compare方法的API描述和compareTo的完全相同,故可以用上一种方法实现。
但不难发现,Comparator的声明前有一个@FunctionalInterface的标识,在API中的描述为:
Java8中,lambda表达式可以被当作Object对象,叫做"target type"目标类型,而目标类型可以是"Functional Interface"函数式接口。它的定义为:
一个接口,如果只有一个显式声明的抽象方法,那么它就是一个函数式接口。
虽然Comparator中不止一个方法声明,但static和default的方法不属于“显式声明的抽象方法”的范畴,而equals方法继承自Object,故只有一个compare方法是显式声明的抽象方法,Comparator符合函数式接口的定义。
下面是使用函数式接口构造lambda表达式的简单实现,其原理暂不讨论。
3. lambda表达式指定比较规则
在lambda表达式中实现一个临时的Comparator,作为参数传递给排序方法:
Collections.sort(list,new Comparator<Interval>(){
@Override
pubilc int compare(Interval o1, Interval o2){
Long l = Long.valueOf(o1.getStart());
return l.compareTo(o2.getStart());
}
});
三、小结
前两种方法都是在目标类中实现用于比较的接口,Comparable或Comparator,这种方法需要了解目标类的内部实现,而且可维护性很差,优点在于只用实现一次,即目标类已变成了可比较类。
第三种方法使用lambda表达式临时定义比较规则,不需要知道目标类的内部实现,而且相当灵活,但是这种方法定义的规则是临时的,可复用性看起来很差。