java中基于comparable接口和比较器comparator的对象比较

Posted 满眼*星辰

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java中基于comparable接口和比较器comparator的对象比较相关的知识,希望对你有一定的参考价值。

引出问题

优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较

我们现在有这个代码,需要用优先级队列加入相同的卡片

class Card{
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public String toString() {
        return "Card{" +
                "rank=" + rank +
                ", suit='" + suit + '\\'' +
                '}';
    }
}

public class Work1 {

    public static void main(String[] args) {
        Card card1 = new Card(1,"♦");
        Card card2 = new Card(1,"♦");

        PriorityQueue<Card> qu = new PriorityQueue<Card>();
        qu.offer(card1);
        qu.offer(card2);
        System.out.println(qu);
    }

}

此时运行程序就会报这样一个错误

在这里插入图片描述

显示我们没有转换成 comparable 接口

那么我们用什么方式来实现此接口呢?

方法一:给Card类实现comparable接口

我们将 Card 类进行改造,实现 comparable 接口,并重写里面的 compare to 方法,具体代码如下:

class Card implements Comparable<Card>{
    public int rank; // 数值
    public String suit; // 花色
    public Card(int rank, String suit) {
        this.rank = rank;
        this.suit = suit;
    }

    @Override
    public String toString() {
        return "Card{" +
                "rank=" + rank +
                ", suit='" + suit + '\\'' +
                '}';
    }

    @Override
    public int compareTo(Card o) {
        return this.rank - o.rank;
    }
}

这样我们再去运行程序,就会发现卡片已经可以成功插入进优先级队列中了
在这里插入图片描述

方法二:用匿名函数实现comparator接口

我们可以在优先级队列创建的时候,实现comparable接口,并重写里面的compare方法,具体代码如下

        PriorityQueue<Card2> qu = new PriorityQueue<Card2>(new Comparator<Card2>() {
            @Override
            public int compare(Card2 o1, Card2 o2) {
                return o1.rank - o2.rank;
            }
        });

此时我们也可以运行此程序,依然没有任何问题

方法三:编写通用比较器类实现comparator接口

编写通用比较类RankComparator来实现comparator

class RankComparator implements Comparator<Card2> {
    @Override
    public int compare(Card2 o1, Card2 o2) {
        return o1.rank - o2.rank;
    }
}

//在主方法里编写
PriorityQueue<Card2> qu = new PriorityQueue<Card2>(new RankComparator());

这样也是可以的

注意事项

看到这里很多小伙伴就会有疑问,为什么我平常使用优先级队列,里面放Integer类型,也没见要实现compareator接口呀

    public static void main(String[] args) {
        PriorityQueue<Integer> queue = new PriorityQueue<>();
        queue.offer(1);
        queue.offer(2);
        System.out.println(queue);
    }

确实也没有异常
在这里插入图片描述
是这样子的,我们Integer的源码,就会发现

Integer这个类是实现Comparable接口的
在这里插入图片描述
里面也实现了compare to方法
在这里插入图片描述
所以,我们平常在往队列里面插入有类型的元素时,不用实现comparable接口,但是在插入自定义类型的时候,就需要考虑怎么比较了

对象的比较

重写compareTo方法

我们知道,对象引用之间,是不能用 > < 这样来比较的,那么我们应该怎么进行比较呢?

我们刚才已经给Card类实现了Comparable接口,并重写了Compare to方法,我们就可以用这个compareTo方法来进行比较

        Card card1 = new Card(4,"♦");
        Card card2 = new Card(2,"♦");
        System.out.println(card1.compareTo(card2));

随后就会去根据我们写的compareTo方法的逻辑进行比较

    @Override
    public int compareTo(Card o) {
        return this.rank - o.rank;
    }

我这里是大于0,则card1 > card2,小于0则相反

重写compare方法

对象比较除了可以用compareable接口来实现比较方法
还可以用comparator比较器里面的compare方法来实现

		class RankComparator implements Comparator<Card2> {
		    @Override
		    public int compare(Card2 o1, Card2 o2) {
		        return o1.rank - o2.rank;
		    }
		}

        RankComparator rankComparator = new RankComparator();
        System.out.println(rankComparator.compare(card1,card2));

在这里插入图片描述
这样我们用比较器compator中的compare方法也可以进行对象引用的比较大小

重写equals方法

        Card card2 = new Card(2,"♦");
        Card card3 = new Card(2,"♦");

 		System.out.println(card2 == card3);
        System.out.println(card2.equals(card3));

在这里插入图片描述

此时不管用 == 还是用 equals 方法,两者都会返回false,这是因为card2和card3是我分别创建的两个引用,两者肯定不是同一个引用,就像两张♦2,虽然花色和数字一样,但是两张牌就是两张牌,不会是同一张牌

小伙伴肯定又要问啦,equals方法不就是比较两者的内容是否一致吗,这两个明明是一致的呀,我之前用String应该和这是一个道理把?

哈哈,这就要说到这个标题“重写equals方法”了,这是因为我们这是一个自定义的Card类,而不像是String引用类已经实现了equals方法了,所以我们需要手动覆写equals方法。

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Card card = (Card) o;
        return rank == card.rank &&
                Objects.equals(suit, card.suit);
    }

此时,我们再运行刚才的代码

在这里插入图片描述
可以看到,用“==”还是一样的false,没什么好解释的了
但是用“equals”方法进行比较,就会比较里面的花色和数字,两者是一样的,自然就返回true 了

三种方式对比

  • Object.equals:因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否
  • Comparable.compareTo:需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序
  • Comparator.compare:需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强

以上是关于java中基于comparable接口和比较器comparator的对象比较的主要内容,如果未能解决你的问题,请参考以下文章

Java中的Comparable接口和Comparator接口

数据结构Java版对象的比较之Comparable与Comparator比较器

Java 中 Comparable 和 Comparator 比较

java中Comparable和Comparator区别和总结

转载Java中Comparable和Comparator比较

java比较器Comparable接口和Comaprator接口的比较