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


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

01、Comparable

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

public interface Comparable<T> {
    int compareTo(T t);
}

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

public class Cmower implements Comparable<Cmower> {
    private int age;
    private String name;

    public Cmower(int age, String name) {
        this.age = age;
        this.name = name;
    }

    @Override
    public int compareTo(Cmower o) {
        return this.getAge() - o.getAge();
    }

    public static void main(String[] args) {
        Cmower wanger = new Cmower(19,"沉默王二");
        Cmower wangsan = new Cmower(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 就复杂的多了,不过,核心的方法只有两个,来看一下源码。

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

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

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

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

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

1)原封不动的 Cmower 类。

public class Cmower  {
    private int age;
    private String name;

    public Cmower(int age, String name) {
        this.age = age;
        this.name = name;
    }
}

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

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

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

public class CmowerComparator implements Comparator<Cmower> {
    @Override
    public int compare(Cmower o1, Cmower o2) {
        return o1.getAge() - o2.getAge();
    }
}

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

public class CmowerNameComparator implements Comparator<Cmower> {
    @Override
    public int compare(Cmower o1, Cmower o2) {
        if (o1.getName().hashCode() < o2.getName().hashCode()) {
            return -1;
        } else if (o1.getName().hashCode() == o2.getName().hashCode()) {
            return 0;
        }
        return 1;
    }
}

3)再来看测试类。

Cmower wanger = new Cmower(19,"沉默王二");
Cmower wangsan = new Cmower(16,"沉默王三");
Cmower wangyi = new Cmower(28,"沉默王一");

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

list.sort(new CmowerComparator());

for (Cmower c : 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

04、鸣谢

好了,各位读者朋友们,以上就是本文的全部内容了。能看到这里的都是最胖的程序员(不,最棒),升职加薪就是你了

来吧,一文彻底搞懂Java中的Comparable和Comparator的更多相关文章

  1. 一文彻底搞懂Java中的环境变量

    一文搞懂Java环境变量 记得刚接触Java,第一件事就是配环境变量,作为一个初学者,只知道环境变量怎样配,在加上各种IDE使我们能方便的开发,而忽略了其本质的东西,只知其然不知其所以然,随着不断的深 ...

  2. 来吧,一文彻底搞懂Java中最特殊的存在——null

    没事的时候,我并不喜欢逛 P 站,而喜欢逛 programcreek 这些技术型网站,于是那天晚上,在夜深人静的时候,我就发现了一个专注基础但不容忽视的主题.比如说:Java 中的 null 到底是什 ...

  3. 轻松搞懂Java中的自旋锁

    前言 在之前的文章<一文彻底搞懂面试中常问的各种“锁”>中介绍了Java中的各种“锁”,可能对于不是很了解这些概念的同学来说会觉得有点绕,所以我决定拆分出来,逐步详细的介绍一下这些锁的来龙 ...

  4. 一文搞懂--Java中重写equals方法为什么要重写hashcode方法?

    Java中重写equals方法为什么要重写hashcode方法? 直接看下面的例子: 首先我们只重写equals()方法 public class Test { public static void ...

  5. 一文搞懂 Java 中的枚举,写得非常好!

    知识点 概念 enum的全称为 enumeration, 是 JDK 1.5 中引入的新特性. 在Java中,被 enum关键字修饰的类型就是枚举类型.形式如下: enum Color { RED, ...

  6. 一篇文章让你搞懂Java中的静态代理和动态代理

    什么是代理模式 代理模式是常用的java设计模式,在Java中我们通常会通过new一个对象再调用其对应的方法来访问我们需要的服务.代理模式则是通过创建代理类(proxy)的方式间接地来访问我们需要的服 ...

  7. java 中的 Comparable 和 Comparator 与 Iterable 和 Iterator

    Comparable 和 Comparator Comparable 和 Comparator 是两个关系不大的类,其分别侧重于不同的方面. 其中,接口 Comparable<T> 强行对 ...

  8. 【软件构造】(转)Java中的comparable和comparator

    为了方便阅读和复习,转载至此,原地址:温布利往事的博客 阅读目录 一.Comparable简介 二.Comparator简介 三.Comparable和Comparator区别比较 回到顶部 一.Co ...

  9. 彻底搞懂Java中equals和==的区别

    java当中的数据类型和“==”的含义: 1.基本数据类型(也称原始数据类型) :byte,short,char,int,long,float,double,boolean.他们之间的比较,应用双等号 ...

随机推荐

  1. python unittest 框架添加测试用例及运行

    找出要测试的testcase,并加入到Testsuite,运行Testsuite并把结果给TestResult1.创建TestSuite实例对象suite = unittest.TestSuite() ...

  2. H3C 以太网集线器

  3. H3C 局域网与OSI参考模型

  4. Lua环境搭建之使用EditPlus搭建Lua开发环境

    luatools正则表达式extension工具encoding 一.语法高亮 打开 EditPlus,Tools 工具-->Preferences首选项==>setting & ...

  5. [Ramda] Handle Errors in Ramda Pipelines with tryCatch

    Handling your logic with composable functions makes your code declarative, leading to code that's ea ...

  6. HDU 5912 Fraction(模拟)

    Problem Description Mr. Frog recently studied how to add two fractions up, and he came up with an ev ...

  7. SpringBoot: 浅谈文件上传和访问的坑 (MultiPartFile)

    本次的项目环境为 SpringBoot 2.0.4, JDK8.0. 服务器环境为CentOS7.0, Nginx的忘了版本. 前言 SpringBoot使用MultiPartFile接收来自表单的f ...

  8. JS的防抖与节流学习笔记

    防抖(debounce):当持续触发事件时,在一定的时间段内,只有最后一次触发的事件才会执行. 例: function debounce(fn, wait) { var timer = null; r ...

  9. linux内核指针和错误值

    很多内部内核函数返回一个指针值给调用者. 许多这些函数也可能失败. 大部分情况, 失 败由返回一个 NULL 指针值来指示. 这个技术是能用的, 但是它不能通知问题的确切特性. 一些接口确实需要返回一 ...

  10. video视频标签一些设置,包括封面、播放结束后的封面、视频占满屏幕的方式、视频播放暂停、展示控制栏、触发全屏播放事件

    video视频标签一些设置,包括封面.播放结束后的封面.视频占满屏幕的方式.视频链接.视频播放暂停.展示控制栏.触发全屏播放事件 <video id="video" auto ...