什么是比较?
在计算机领域中,比较分为两种:
- 身份的比较
- 值的比较
而在Java中主要有三种比较方式:
- Object.equals
- Comparable
- Comparator
下面我们逐个总结:
1. == VS equals
p == q 表示的是 p 和 q 两个引用指向同一个对象
p.equals(q) 表示 p 指向的对象 和 q 指向的对象是否是值语义相等的
覆写前
public class Card {
public String rank;
public String suit;
public Card(String rank, String suit) {
this.rank = rank;
this.suit = suit;
}
public static void main(String[] args) {
Card card1 = new Card("A", "♥️");
Card card2 = new Card("K", "♦️");
Card c = card1;
card1.equals(card2);//false 因为不覆写 equals,默认的 equals逻辑就是引用比较
card1.equals(c);//true 因为不覆写 equals,默认的 equals逻辑就是引用比较
}
}
覆写后
public class Card {
public int rank;
public String suit;
public Card(int rank, String suit) {
this.rank = rank;
this.suit = suit;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || !(o instanceof Card)) {
return false;
}
Card c = (Card)o;
return this.rank == c.rank
&& this.suit.equals(c.suit) ;
}
public static void main(String[] args) {
Card card1 = new Card(5, "♦️");
Card card2 = new Card(5, "♥️");
Card c = card1;
card1.equals(card2); // true
card1.equals(c); //true
}
}
- 如果指向同一个对象,返回 true
- 如果传入的为 null,返回 false
- 如果传入的对象类型不是 Card,返回 false
- 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
- 注意下调用其他引用类型的比较也需要 equals,例如这里的 suit 的比较
2. Comparable
我们在使用 Comparable 接口的时候要实现它的 compareTo 方法
compareTo 的返回值
- // < 0: 表示 this 指向的对象小于 o2 指向的对象
- // == 0: 表示 this 指向的对象等于 o2指向的对象
- // > 0: 表示 this 指向的对象等于 o2 指向的对象
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;
}
public static void main(String[] args) {
Card card1 = new Card(1, "♦️");
Card card2 = new Card(2, "♥️");
Card card3 = new Card(1, "♦️");
card1.compareTo(card2); // < 0 表示 card1 比较小
card1.compareTo(card3); // == 0 表示 相等
card2.compareTo(card3); // > 0 表示 card2 比较大
}
}
3. Comparator
我们在使用 Comparator 接口的时候要先在主类实现 Comparable接口,和comparaTo 方法。然后在创建一个类来实现 Comparator 接口,再实现compare 方法。
compare 的返回值
- // < 0: 表示 o1 指向的对象小于 o2 指向的对象
- // == 0: 表示 o1 指向的对象等于 o2指向的对象
- // > 0: 表示 o1 指向的对象等于 o2 指向的对象
import java.util.Comparator;
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;
}
public static void main(String[] args) {
Card card1 = new Card(1, "♦️");
Card card2 = new Card(2, "♥️");
Card card3 = new Card(1, "♦️");
Comparator<Card> comparator = new CardComparator();
comparator.compare(card1, card2); // = 0,表示 牌大小相等
comparator.compare(card1, card3); // < 0,表示 card1 比较小
comparator.compare(card2, card3); // > 0,表示 card1 比较大
}
}
class CardComparator implements Comparator<Card> {
@Override
public int compare(Card o1, Card o2) {
if (o1 == o2) {
return 0;
}
if (o1 == null) {
// o2 一定不是 null
return -1;
}
if (o2 == null) {
return 1;
}
return o1.rank - o2.rank;
}
}
三种比较方法的比较
覆写的方法 | 说明 |
---|---|
Object.equals | 因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否 |
Comparable.compareTo | 需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内 部顺序 |
Comparator.compare | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强 |
和Java集合框架的配合
- 使用 contains 类似的方法,内部基本在调用元素的 equals 方法,所以要求元素覆写过 equals 方法
- 使用 HashMap,key 的比较内部会调用 equals 方法,所以要求元素覆写过 equals 方法
- 使用排序相关方法,内部需要进行比较,所以或者选择实现Comparable 或者传入一个 Comparator
- 使用 TreeMap,key 需要进行大小比较,所以或者选择实现 Comparable 或者传入一个 Comparator