文章目录
- 一、PriorityQueue中插入对象
- 二、元素的比较
- 2.1、基本类型的比较
- 2.2、对象比较的问题
- 三、对象的比较
- 3.1、覆写基类的equals
- 3.2、基于Comparable接口类的比较
- 3.3、基于比较器比较
- 3.4、三种方式对比
一、PriorityQueue中插入对象
前篇我们讲解了优先级队列,大家应该还记得,其内部是按照小根堆的方式实现的,插入元素的前提是不为null或者元素必须能够比较,为了方便我们当时插入的为Integer类型的元素,那如果我们插入自定义类型对象呢?
class Card {
public int rank; // 数值
public String suit; // 花色
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
}
public class TestPriorityQueue {
public static void TestPriorityQueue()
{
PriorityQueue<Card> p = new PriorityQueue<>();
p.offer(new Card(1, "♠"));
p.offer(new Card(2, "♠"));
}
public static void main(String[] args) {
TestPriorityQueue();
}
}
如上代码,优先级队列的底层使用堆,向堆中插入元素时,为了满足堆的性质,必须要进行元素的比较,而此时Card为自定义数据类型,是没有办法直接进行比较的,因此抛出异常。
那么怎么做才能比较自定义数据类型呢?我们往下看:
二、元素的比较
2.1、基本类型的比较
在Java中,基本类型的元素是可以直接比较大小的,如下(注释为输出结果):
2.2、对象比较的问题
class Card {
public int rank; // 数值
public String suit; // 花色
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
}
public class text {
public static void main(String[] args) {
Card c1 = new Card(1, "♠");
Card c2 = new Card(1, "♠");
Card c3 = c1;
}
}
1、c1==c2:
c1,c2分别new了一个Card类型的对象,分别指向不同的对象,其地址也不同,所以会打印false
2、c1.equals(c2):
我们的Card类默认继承Object类的,Object类中实现了equals方法,所以我们能够直接调用,但为什么是false呢?我们点进源码看到,其返回的就是c1==c2
3、c1==c3:
c1和c3指向同一个对象,过其返回true
那么当我们用>或<符号进行比较时,会发生什么?
我们可看出Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较,为什么可以使用==进行比较呢?
因为:
对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equal方法,而==默认情况下调 用的就是equal方法,但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地址。
三、对象的比较
3.1、覆写基类的equals
那么,我们在Card中重写Object类中的equals方法:
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);
}
我们发现c1.equals(c2)就返回true
重写的equals:
1. 如果指向同一个对象,返回 true
2. 如果传入的为 null,返回 false
3. 如果传入的对象类型不是 Card,返回 false
4. 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
5. 注意下调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较
缺陷:equal 只能按照相等进行比较,不能按照大于、小于的方式进行比较。
3.2、基于Comparable接口类的比较
对于用户自定义的数据类型,如果想按照大小与方式进行比较时:在定义类时,实现Comparble接口即可,然后在类中重写compareTo方法。
public 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;
}
return this.rank - o.rank;
}
}
我们就按照期待的结果(按照数值比较)成功比较出c1和c2的数值大小关系 :
注意:Compareble是java.lang中的接口类,可以直接使用。
3.3、基于比较器比较
上述我们可以比较自定义类型的数值,那么花色的比较要实现Comparator接口,重写compare方法:
class suitComparator implements Comparator<Card> {
@Override
public int compare(Card o1, Card o2) {
return o1.suit.compareTo(o2.suit);
}
}
public static void main(String[] args) {
Card c1=new Card(12,"♥");
Card c2=new Card(13,"♠");
suitComparator com=new suitComparator();
if(com.compare(c1,c2)>0){
System.out.println("c1 > c2");
}else{
System.out.println("c1 < c2");
}
if(com.compare(c1,c3)>0){
System.out.println("c1 > c3");
}else{
System.out.println("c1 < c3");
}
if(com.compare(c2,c3)>0){
System.out.println("c2 > c3");
}else{
System.out.println("c2 < c3");
}
}
//结果:
// c1 > c2
// c1 > c3
// c2 < c3
我们就成功比较出Card类对象的花色大小。
3.4、三种方式对比
Comparable是Java中一个接口,它用于实现对象的比较。如果一个类实现了Comparable接口,就意味着该类的对象是可以排序的。Comparable接口中只有一个方法compareTo(Object o),该方法用于比较对象的大小关系。
equals是Java中一个方法,用于比较两个对象是否相等。equals方法实际上是用来比较两个对象的内容是否相等,而不是比较对象的引用是否相等。equals方法通常会被重写,以实现自定义的相等逻辑。
Comparator是Java中一个接口,它可以用于实现两个对象的比较。Comparator接口中有一个方法compare(Object o1, Object o2),该方法用于比较o1和o2的大小关系。Comparator接口可以用于对任意的对象进行排序,而不需要修改对象的定义,因此它具有更大的灵活性。