今天在逛 programcreek 的时候,我发现了一些专注细节但价值连城的主题。比如说:Java 的 Comparable 和 Comparator 是兄弟俩吗?像这类灵魂拷问的主题,非常值得深入地研究一下。

Comparable 和 Comparator 是 Java 的两个接口,从名字上我们就能够读出来它们俩的相似性:以某种方式来比较两个对象。但它们之间到底有什么区别呢?请随我来,打怪进阶喽!

01、Comparable

Comparable 接口的定义非常简单,源码如下所示。

publicinterfaceComparable<T>{
intcompareTo(Tt);
}
复制代码

如果一个类实现了 Comparable 接口(只需要干一件事,重写compareTo()方法),就可以按照自己制定的规则将由它创建的对象进行比较。下面给出一个例子。

publicclassCmowerimplementsComparable<Cmower>{
privateintage;
privateStringname;

publicCmower(intage,Stringname){
this.age=age;
this.name=name;
}

@Override
publicintcompareTo(Cmowero){
returnthis.getAge()-o.getAge();
}

publicstaticvoidmain(String[]args){
Cmowerwanger=newCmower(19,"沉默王二");
Cmowerwangsan=newCmower(16,"沉默王三");

if(wanger.compareTo(wangsan)<0){
System.out.println(wanger.getName()+"比较年轻有为");
}else{
System.out.println(wangsan.getName()+"比较年轻有为");
}
}
}
复制代码

在上面的示例中,我创建了一个 Cmower 类,它有两个字段:age 和 name。Cmower 类实现了 Comparable 接口,并重写了compareTo()方法。

程序输出的结果是“沉默王三比较年轻有为”,因为他比沉默王二小三岁。这个结果有什么凭证吗?

凭证就在于compareTo()方法,该方法的返回值可能为负数,零或者正数,代表的意思是该对象按照排序的规则小于、等于或者大于要比较的对象。如果指定对象的类型与此对象不能进行比较,则引发ClassCastException异常(自从有了泛型,这种情况就少有发生了)。

02、Comparator

Comparator 接口的定义相比较于 Comparable 就复杂的多了,不过,核心的方法只有两个,来看一下源码。

publicinterfaceComparator<T>{
intcompare(To1,To2);
booleanequals(Objectobj);
}
复制代码

第一个方法compare(T o1, T o2)的返回值可能为负数,零或者正数,代表的意思是第一个对象小于、等于或者大于第二个对象。

第二个方法equals(Object obj)需要传入一个 Object 作为参数,并判断该 Object 是否和 Comparator 保持一致。

有时候,我们想让类保持它的原貌,不想主动实现 Comparable 接口,但我们又需要它们之间进行比较,该怎么办呢?

Comparator 就派上用场了,来看一下示例。

1)原封不动的 Cmower 类。

publicclassCmower{
privateintage;
privateStringname;

publicCmower(intage,Stringname){
this.age=age;
this.name=name;
}
}
复制代码

(说好原封不动,getter/setter 吃了啊)

Cmower 类有两个字段:age 和 name,意味着该类可以按照 age 或者 name 进行排序。

2)再来看 Comparator 接口的实现类。

publicclassCmowerComparatorimplementsComparator<Cmower>{
@Override
publicintcompare(Cmowero1,Cmowero2){
returno1.getAge()-o2.getAge();
}
}
复制代码

按照 age 进行比较。当然也可以再实现一个买QQ靓号比较器,按照 name 进行自然排序,示例如下。

publicclassCmowerNameComparatorimplementsComparator<Cmower>{
@Override
publicintcompare(Cmowero1,Cmowero2){
if(o1.getName().hashCode()<o2.getName().hashCode()){
return-1;
}elseif(o1.getName().hashCode()==o2.getName().hashCode()){
return0;
}
return1;
}
}
复制代码

3)再来看测试类。

Cmowerwanger=newCmower(19,"沉默王二");
Cmowerwangsan=newCmower(16,"沉默王三");
Cmowerwangyi=newCmower(28,"沉默王一");

List<Cmower>list=newArrayList<>();
list.add(wanger);
list.add(wangsan);
list.add(wangyi);

list.sort(newCmowerComparator());

for(Cmowerc:list){
System.out.println(c.getName());
}
复制代码

创建了三个对象,age 不同,name 不同,并把它们加入到了 List 当中。然后使用 List 的sort()方法进行排序,来看一下输出的结果。

沉默王三
沉默王二
沉默王一
复制代码

这意味着沉默王三的年纪比沉默王二小,排在第一位;沉默王一的年纪比沉默王二大,排在第三位。和我们的预期完全符合。

03、到底该用哪一个呢?

通过上面的两个例子可以比较出 Comparable 和 Comparator 两者之间的区别:

一个类实现了 Comparable 接口,意味着该类的对象可以直接进行比较(排序),但比较(排序)的方式只有一种,很单一。一个类如果想要保持原样,又需要进行不同方式的比较(排序),就可以定制比较器(实现 Comparator 接口)。Comparable 接口在java.lang包下,而Comparator接口在java.util包下,算不上是亲兄弟,但可以称得上是表(堂)兄弟。

举个不恰当的例子。我想从洛阳出发去北京看长城,体验一下好汉的感觉,要么坐飞机,要么坐高铁;但如果是孙悟空的话,翻个筋斗就到了。我和孙悟空之间有什么区别呢?孙悟空自己实现了 Comparable 接口(他那年代也没有飞机和高铁,没得选),而我可以借助 Comparator 接口(现代化的交通工具)。

总而言之,如果对象的排序需要基于自然顺序,请选择Comparable,如果需要按照对象的不同属性进行排序,请选择Comparator。


更多相关文章

  1. 重学 Kotlin —— object,史上最 “快” 单例 ?
  2. 切记,这样打日志定位 Bug 又快又准!
  3. Spring Boot + EasyExcel 导入导出,好用到爆!
  4. 抽象类与接口、后期静态绑定与魔术方法简单总结
  5. 面向对象 oop 与类的进阶
  6. PHP基础:PHP抽象类和接口、类的重载和方法的重载
  7. 使用面向对象方法实现用户信息增删改查
  8. 静态、抽象、类常量、接口、后期静态绑定以及魔术方法
  9. 回答两个被频繁问到的代码写法问题

随机推荐

  1. android 开发的一个小警告
  2. Android实现新浪微博
  3. 改写ListView样式
  4. Android中如何使用 address2line
  5. android 系统自带主题样式及自定义主题样
  6. Android对应版本号
  7. 权重
  8. 有关XML的点击状态背景图的设置
  9. android 的中文意思
  10. Android 仿QQ多级列表框实现