1.基本类型的比较
在Java中,常见的基本类型比如int、char、boolean类型都可以直接比较。
代码示例:
public class Demo { public static void main(String[] args) { int a=10; int b=20; System.out.println(a>b); System.out.println(a<b); System.out.println(a==b); char c1='A'; char c2='B'; System.out.println(c1>c2); System.out.println(c1<c2); System.out.println(c1==c2); boolean b1=true; boolean b2=false; System.out.println(b1==b2); System.out.println(b1!=b2); } } // false true false false true false false true Process finished with exit code 0
2.对象的比较
Java中引用类型的变量不能直接按照 > 或者 < 方式进行比较,但可以用==来比较,因为对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equals方法,而 == 默认情况下调用的就是equals方法,但该方法比较的是引用对象的地址,在有些情况下需要比较的是对象中的内容,下边我们就通过扑克牌的比较例子来具体说明这个问题。
// Object中equals的实现,可以看到:直接比较的是两个引用变量的地址 public boolean equals(Object obj) { return (this == obj); }
2.1 重写基类的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; //如果为null或类型不匹配 if (o == null || getClass() != o.getClass()) return false; Card card = (Card) o; //数字和花色都相同才返回true return rank == card.rank && Objects.equals(suit, card.suit); } @Override public int hashCode() { return Objects.hash(rank, suit); } }
注意:
- 如果指向同一个对象,返回 true
- 如果传入的为 null,返回 false
- 如果传入的对象类型不是 Card,返回 false
- 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌
- 存在的缺陷:
- equals只能按照相等进行比较,不能按照大于、小于的方式进行比较。
2.2 实现Comparble接口
Comparble是JDK提供的泛型的比较接口类,源码实现具体如下:
public interface Comparable<E> { // 返回值: // < 0: 表示 this 指向的对象小于 o 指向的对象 // == 0: 表示 this 指向的对象等于 o 指向的对象 // > 0: 表示 this 指向的对象大于 o 指向的对象 int compareTo(E o); }
对用用户自定义类型,如果要想按照大小与方式进行比较时:在定义类时,实现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; } //根据数值比较 @Override public int compareTo(Card o) { return this.rank-o.rank; } public static void main(String[] args) { Card c1=new Card(1,"♠"); Card c2=new Card(2,"♣"); System.out.println(c1.compareTo(c2)); } } // -1 Process finished with exit code 0
存在的缺陷:
对类的侵入性较大,让类实现Comparble接口重写compareTo方法后就指定了比较的方式(指定了用于比较的属性),若对象想要通过其他的属性来进行比较时就无法实现了。
2.3 比较器(实现Comparator接口)
通过调用下边这个接口就可以实现对象中指定属性的比较,这样就将需要比较的类的属性从原类中抽取出来,对类的侵入性大大减小。
用户自定义比较器类,实现Comparator接口
public interface Comparator<T> { // 返回值: // < 0: 表示 o1 指向的对象小于 o2 指向的对象 // == 0: 表示 o1 指向的对象等于 o2 指向的对象 // > 0: 表示 o1 指向的对象等于 o2 指向的对象 int compare(T o1, T o2); }
覆写Comparator中的compare方法
public class Card { public int rank;//数字 public String suit;//花色 public Card(int rank,String suit) { this.rank=rank; this.suit=suit; } static class RankComparator implements Comparator<Card> { @Override public int compare(Card o1, Card o2) { return o1.rank-o2.rank; } } static 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(1,"♠"); Card c2=new Card(2,"♣"); RankComparator rankComparator = new RankComparator(); SuitComparator suitComparator = new SuitComparator(); System.out.println(rankComparator.compare(c1,c2)); System.out.println(suitComparator.compare(c1,c2)); } } // -1 -3 Process finished with exit code 0
2.4 总结
覆写的方法 | 说明 |
Object.equals | 因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否 |
Comparable.compareTo | 需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序 |
Comparator.compare | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强 |
3.集合框架中PriorityQueue的比较
集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比大小,PriorityQueue采用了:
Comparble和Comparator两种方式。
- 1.Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法
- 2.用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法。
JDK中PriorityQueue的实现:
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable { // ... // 默认容量 private static fifinal int DEFAULT_INITIAL_CAPACITY = 11; // 内部定义的比较器对象,用来接收用户实例化PriorityQueue对象时提供的比较器对象 private fifinal Comparator<? super E> comparator; // 用户如果没有提供比较器对象,使用默认的内部比较,将comparator置为null public PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } // 如果用户提供了比较器,采用用户提供的比较器进行比较 public PriorityQueue(int initialCapacity, Comparator<? super E> comparator) { // Note: This restriction of at least one is not actually needed, // but continues for 1.5 compatibility if (initialCapacity < 1) throw new IllegalArgumentException(); this.queue = new Object[initialCapacity]; this.comparator = comparator; } // ... // 向上调整: // 如果用户没有提供比较器对象,采用Comparable进行比较 // 否则使用用户提供的比较器对象进行比较 private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); } // 使用Comparable @SuppressWarnings("unchecked") private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { int parent = (k - 1) >>> 1; Object e = queue[parent]; if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; } // 使用用户提供的比较器对象进行比较 @SuppressWarnings("unchecked") private void siftUpUsingComparator(int k, E x) { while (k > 0) { int parent = (k - 1) >>> 1; Object e = queue[parent]; if (comparator.compare(x, (E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = x; } }