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区别和总结