Comparable和Comparator接口的差异

Comparable和Comparator接口

问题提出

优先级队列也就是堆,在插入元素时有个要求:插入的元素不能是null或者元素之间必须能够进行比较,为了简单起见只是在优先级队列中插入Integer类型,那么当我们想插入自定义的对象时,能不能实现呢?

//定义扑克牌内部类
class Card{
    public int rank;  //代表牌的数值
    public String suit; //代表牌的花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}
public class TestDemo {
    public static void main(String[] args) {
        fun();
    }

    public static void fun(){
        PriorityQueue<Card> priorityQueue = new PriorityQueue<>();
        //将Card对象加入优先级队列
        priorityQueue.offer(new Card(1,"♥"));
        priorityQueue.offer(new Card(2,"♣"));
    }
}

来看一下main执行结果:

Comparable和Comparator接口的差异
程序抛出了类型不兼容引发的运行时异常,也就是说

这是无法实现的,为了满足堆的性质,必须进行元素的比较,而Card类型对象是无法进行比较的,因此抛出异常。

引用类型对象比较

基本数据类型比较

在Java语言中,基本数据类型可以直接比较大小

public static void main(String[] args) {
        //整型
        int a = 66;
        int b = 88;
        System.out.println(a > b);
        System.out.println(a == b);
        System.out.println(a < b);
        //字符型
        char c = 'A';
        char d = 'B';
        System.out.println(c > d);
        System.out.println(c == d);
        System.out.println(c < d);
        //布尔型
        boolean e = true;
        boolean f = false;
        System.out.println(e == f);
        System.out.println(e != f);
    }

Comparable和Comparator接口的差异

对象的比较

先看一段代码

Comparable和Comparator接口的差异
可以看出对Card类型对象按 < 或者 > 方式进行比较时,编译会出错,也就说明引用类型变量是无法进行大小比较的;
Comparable和Comparator接口的差异
Comparable和Comparator接口的差异
但是我们发现,使用==却没有编译报错,这是因为Java中规定所有类都默认继承父类Object,而Object类提供了equals方法,默认情况下==调用的是equals方法,该方法的比较规则是:不比较对象的内容,直接比较对象的第地址。c1和c3指向的是同一对象,所以比较结果为true。

那么如何比较对象的内容?大致三种方式:重写equals、实现Comparable接口、实现Comparator接口

1.重写equals方法

@Override
    public boolean equals(Object obj) {
        //重写equals
        if(this == obj){
            return true;
        }
        
        //obj为null或者obj不是Card子类对象
        if(obj == null || obj instanceof Card){
            return false;
        }
        
        //基本数据类型可以直接比较,引用类型最好还是调用equals
        Card c = (Card)obj;
        return rank == c.rank && suit.equals(c.suit);
    }

缺点:只能以相等的方式进行比较,不能按照大于、小于的方式对对象进行比较。

2.Comparable接口

看一下java.lang.Comparable接口源码,在这个接口中,只存在一个抽象方法compareTo:
Comparable和Comparator接口的差异Comparable和Comparator接口的差异

定义方法返回值为int,源码定义的比较规则:
< 0:表示this指向的对象小于o指向的对象
== 0:表示this指向的对象等于o指向的对象
0:表示this指向的对象大于o指向的对象

知道了源码中怎么设置比较大小规则后,对于我们自定义的类型而言,如果要按照大小方式进行比较,让自定义的类实现Comparable接口并重写compareTo方法。

class Card implements Comparable<Card>{
    public int rank;  //代表牌的数值
    public String suit; //代表牌的花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    //根据牌的数值比较,不管牌的花色,认为null对象是最小的
    @Override
    public int compareTo(Card o) {
        if(o == null){
            return 1;
        }else{
            return rank - o.rank;
        }
    }
}
public class TestDemo {

    public static void main(String[] args) {
        Card a = new Card(1,"♠");
        Card b = new Card(2,"♦");
        Card c = new Card(1,"♥");
        System.out.println(a.compareTo(b)); 
        System.out.println(a.compareTo(c));
        System.out.println(b.compareTo(c));
    }
}

Comparable和Comparator接口的差异
从执行结果可以看出,此时我们就可以比较对象之间的大小了

3.Comparator接口

按照Comparator接口进行对象之间的比较,具体步骤如下:

先看一下java.util.Comparator接口源码
Comparable和Comparator接口的差异
Comparable和Comparator接口的差异

定义方法返回值为int,源码定义的比较规则:
< 0:表示o1指向的对象小于o2指向的对象
== 0:表示o1指向的对象等于o2指向的对象
0:表示o1指向的对象大于o2指向的对象

我们可以通过一个自定义类比较器去实现Comparator接口,通过比较器类中compare方法进行对象之间的大小比较。

/**
 * 自定义类
 */
class Card{
    public int rank;  //代表牌的数值
    public String suit; //代表牌的花色

    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }
}

/**
 * 比较器类
 */
class CardComparator implements Comparator<Card>{
    //我们根据牌的数值去比较大小,不管花色
    @Override
    public int compare(Card o1, Card o2) {
        if(o1 == o2) return 0;
        if(o1 == null) return -1;
        if(o2 == null) return 1;
        return o1.rank - o2.rank;
    }
}

public class TestDemo {
    public static void main(String[] args) {
        Card p = new Card(1,"♣");
        Card q = new Card(2,"♦");
        Card o = new Card(1,"♥");

        //定义比较器对象
        CardComparator cardComparator = new CardComparator();
        
        //使用比较器对象进行比较
        System.out.println(cardComparator.compare(q,p));
        System.out.println(cardComparator.compare(p,o));
        System.out.println(cardComparator.compare(o,q));
    }

}

Comparable和Comparator接口的差异
通过实现Comparator接口也完成了对象之间大小比较。

三者的区别

equals:默认所有类都是继承Object类的,因此直接在自定义类中重写该方法就行,但是只能比较两个对象相等与否,不能比较对象大小关系。

compareTo:在自定义类手动实现Comparable接口,侵入性极强,一旦实现,后面使用该类都有顺序。

compare:与实现Comparable接口不同的是,需要额外定义一个比较器类实现Comparator接口,在比较器类中重写compare方法,最后创建比较器类对象去实现自定义类对象之间的大小比较;对待自定义类的侵入性弱,但对算法代码实现侵入性强。

上一篇:SCC重新建图


下一篇:python黑魔法 -- 内置方法使用