Comparable和Comparator的比较

https://www.cnblogs.com/ldy-blogs/p/8488138.html

在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。

在Arrays.sort()和Collections.sort()中

通常对象之间的比较可以从两个方面去看:

第一个方面:对象的地址是否一样,也就是是否引用自同一个对象。

​ 这种方式可以直接使用“==“来完成。

第二个方面:以对象的某一个属性的角度去比较。

对于JDK8而言,有三种实现对象比较的方法:

1、覆写Object类的equals()方法;

2、继承Comparable接口,并实现compareTo()方法;

3、定义一个单独的对象比较器,继承自Comparator接口,实现compare()方法。

由于使用的排序方式的不同,具体选择哪种方法来实现对象的比较也会有所不同。

Comparable 简介

Comparable 是排序接口。

Comparable可以认为是一个内比较器

若一个类实现了Comparable接口,就意味着“该类支持排序”。 即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。

此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。

Comparable 定义

Comparable 接口仅仅只包括一个函数,它的定义如下:

package java.lang;
import java.util.*;

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

说明:
假设我们通过 x.compareTo(y) 来“比较x和y的大小”。
若返回“负数”,意味着“x比y小”;
返回“零”,意味着“x等于y”;
返回“正数”,意味着“x大于y”。

我们采用Arrays.sort()和Collections.sort() 这个方法完成的排序时,实际上要求了被排序的类型

需要实现Comparable接口,完成比较的功能

我们一般见到的类型 都继承了Comparable接口,如:

public final class Integer extends Number implements Comparable<Integer> {..}
 
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {...}

但是这样就把这种规则写死了,比如我想要字符串按照第一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用
public static <T> void sort(List<T> list,Comparator<? super T> ) 方法灵活的完成 。

Comparator 简介

Comparator 是比较器接口。

Comparator可以认为是是一个外比较器

我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。

也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序

Comparator 定义

Comparator 接口仅仅只包括两个个函数,它的定义如下:

package java.util;

public interface Comparator<T> {

    int compare(T o1, T o2);

    boolean equals(Object obj);
}

说明:
(01) 若一个类要实现Comparator接口:
    它一定要实现compareTo(T o1, T o2) 函数,但可以不实现equals(Object obj) 函数。
  为什么可以不实现 equals(Object obj) 函数呢? 
    因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。

(02) int compare(T o1, T o2) 是“比较o1和o2的大小”。
    返回“负数”,意味着“o1比o2小”;
    返回“零”,意味着“o1等于o2”;
    返回“正数”,意味着“o1大于o2”。

Comparable和Comparator的比较

不同博主的总结

"Comparator"和“Comparable”的比较程序。
(01) "Comparable"
    *   它是一个排序接口,只包含一个函数compareTo()。
    *   一个类实现了Comparable接口,就意味着“该类本身支持排序”,它可以直接通过Arrays.sort() 或 Collections.sort()进行排序。
(02) "Comparator"
   *   它是一个比较器接口,包括两个函数:compare() 和 equals()。
   *   一个类实现了Comparator接口,那么它就是一个“比较器”。其它的类,可以根据该比较器去排序。
   *
   
综上所述:Comparable是内部比较器,而Comparator是外部比较器。
一个类本身实现了Comparable比较器,就意味着它本身支持排序;
若它本身没实现Comparable,也可以通过外部比较器Comparator进行排序。

------------------------------------------------------------------------------------

  两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。 
  用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。
  
----------------------------------------------------------------------------
总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:

1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法

2、实现Comparable接口的方式比实现Comparator接口的耦合性 要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。从这个角度说,其实有些不太好,尤其在我们将实现类的.class文件打成一个.jar文件提供给开发者使用的时候。实际上实现Comparator 接口的方式后面会写到就是一种典型的策略模式。

当然,这不是鼓励用Comparator,意思是开发者还是要在具体场景下选择最合适的那种比较器而已。

例子

(1)在List集合中的数字,升序输出一遍,降序输出一遍

public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        list.add(2);
        list.add(1);
        list.add(3);//[2,1,3]

        //默认升序
        Collections.sort(list);//默认升序
        for (Integer integer : list) {
            System.out.print(integer);//123
        }
        System.out.println();

        //降序
        Collections.sort(list,new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2) {//注意:写法
                return o2-o1;
            }
        });
        for (Integer integer : list) {
            System.out.print(integer);//321
        }
    }
-----------
123
321

(2)自定义Student类,有name和id,按照age降序输出(用两种写法)

​ 一、在类中重写Comparable的compareTo方法

//student类
public class Student implements Comparable<Student> {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    //Override重写比较方法
    @Override
    public int compareTo(Student o) {
        return o.age-this.age;//降序
    }
}
//测试
public class TestStudent {
    public static void main(String[] args) {
        List<Student> list=new ArrayList<>();

        list.add(new Student("rose",3));
        list.add(new Student("jack",2));
        list.add(new Student("abc",1));
        list.add(new Student("ace",5));
        list.add(new Student("mark",4));

        //按age的降序排列
        Collections.sort(list);

        for (Student s : list) {
            System.out.println(s);
        }
    }
}
----------------------------------
Student{name='ace', age=5}
Student{name='mark', age=4}
Student{name='rose', age=3}
Student{name='jack', age=2}
Student{name='abc', age=1}    

(2)用Collections.sort(List list,Comparetor c)方式

//Strudent类
public class Student {
    private String name;
    private int age;

    public Student() {
    }

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

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
//测试
public class TestStudent {
    public static void main(String[] args) {
        List<Student> list=new ArrayList<>();

        list.add(new Student("rose",3));
        list.add(new Student("jack",2));
        list.add(new Student("abc",1));
        list.add(new Student("ace",5));
        list.add(new Student("mark",4));

        //按age的降序排列
        Collections.sort(list, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                return o2.getAge()-o1.getAge();
            }
        });

        for (Student s : list) {
            System.out.println(s);
        }
    }
}
----------------------------------
Student{name='ace', age=5}
Student{name='mark', age=4}
Student{name='rose', age=3}
Student{name='jack', age=2}
Student{name='abc', age=1}    
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352