目录
一:为何要比较
说到优先级队列, 优先级队列在插入元素时有个要求:插入的元素不能是 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 是没有办 法直接进行比较的,因此抛出异常。
二:元素的比较
1 基本类型的比较
在 Java 中,基本类型的对象可以直接比较大小。public class TestCompare { 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 ); } }
2 对象的比较
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 main ( String [] args ) { Card c1 = new Card ( 1 , "♠" ); Card c2 = new Card ( 2 , "♠" ); Card c3 = c1 ; //System.out.println(c1 > c2); // 编译报错 System . out . println ( c1 == c2 ); // 编译成功 ----> 打印 false ,因为 c1 和 c2 指向的是不同对象 //System.out.println(c1 < c2); // 编译报错 System . out . println ( c1 == c3 ); // 编译成功 ----> 打印 true ,因为 c1 和 c3 指向的是同一个对象 } }c1 、 c2 和 c3 分别是 Card 类型的引用变量,上述代码在比较编译时: c1 > c2 编译失败 c1== c2 编译成功 c1 < c2 编译失败 从编译结果可以看出, Java 中引用类型的变量不能直接按照 > 或者 < 方式进行比较 。 那为什么 == 可以比较? 因为: 对于用户实现自定义类型,都默认继承自 Object 类,而 Object 类中提供了 equal 方法,而 == 默认情况下调 用的就是 equal 方法 ,但是该方法的比较规则是: 没有比较引用变量引用对象的内容,而是直接比较引用变量的地 址 ,但有些情况下该种比较就不符合题意。
三:对象的比较
有些情况下,需要比较的是对象中的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中内容来调整 堆,那该如何处理呢?1 覆写基类的equal
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 ; } // o 如果是 null 对象,或者 o 不是 Card 的子类 if ( o == null || ! ( o instanceof Card )) { return false ; } // 注意基本类型可以直接比较,但引用类型最好调用其 equal 方法 Card c = ( Card ) o ; return rank == c . rank && suit . equals ( c . suit ); } }注意: 一般覆写 equals 的套路就是上面演示的 1. 如果指向同一个对象,返回 true 2. 如果传入的为 null ,返回 false 3. 如果传入的对象类型不是 Card ,返回 false 4. 按照类的实现目标完成比较,例如这里只要花色和数值一样,就认为是相同的牌 5. 注意下调用其他引用类型的比较也需要 equals ,例如这里的 suit 的比较 覆写基类 equal 的方式虽然可以比较,但缺陷是: equal 只能按照相等进行比较,不能按照大于、小于的方式进行 比较 。
2 基于comparable接口类的比较
Comparble 是 JDK 提供的泛型的比较接口类,源码实现具体如下:对用用户自定义类型,如果要想按照大小与方式进行比较时: 在定义类时,实现 Comparble 接口即可,然后在类 中重写 compareTo 方法。public interface Comparable < E > { // 返回值 : // < 0: 表示 this 指向的对象小于 o 指向的对象 // == 0: 表示 this 指向的对象等于 o 指向的对象 // > 0: 表示 this 指向的对象等于 o 指向的对象 int compareTo ( E o ); }
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 是最小的 @Overridepublic int compareTo ( Card o ) { if ( o == null ) { return 1 ; } return rank - o . rank ; } public static void main ( String [] args ){ Card p = new Card ( 1 , "♠" ); Card q = new Card ( 2 , "♠" ); Card o = new Card ( 1 , "♠" ); System . out . println ( p . compareTo ( o )); // == 0 ,表示牌相等 System . out . println ( p . compareTo ( q )); // < 0 ,表示 p 比较小 System . out . println ( q . compareTo ( p )); // > 0 ,表示 q 比较大 } }
Compareble是java.lang中的接口类,可以直接使用。
3 基于比较器的比较
按照比较器方式进行比较,具体步骤如下:用户自定义比较器类,实现 Comparator接口 public interface Comparator < T > { // 返回值 : // < 0: 表示 o1 指向的对象小于 o2 指向的对象 // == 0: 表示 o1 指向的对象等于 o2 指向的对象 // > 0: 表示 o1 指向的对象等于 o2 指向的对象 int compare ( T o1 , T o2 ); }
注意: 区分 Comparable 和 Comparator 。 覆写 Comparator 中的 compare 方法
import java . util . Comparator ; class Card { public int rank ; // 数值 public String suit ; // 花色 public Card ( int rank , String suit ) { this . rank = rank ; this . suit = suit ; } } class CardComparator implements Comparator < Card > { // 根据数值比较,不管花色 // 这里我们认为 null 是最小的@Override public int compare ( Card o1 , Card o2 ) { if ( o1 == o2 ) { return 0 ; } if ( o1 == null ) { return - 1 ; } if ( o2 == null ) { return 1 ; } return o1 . rank - o2 . rank ; } public static void main ( String [] args ){ Card p = new Card ( 1 , "♠" ); Card q = new Card ( 2 , "♠" ); Card o = new Card ( 1 , "♠" ); // 定义比较器对象 CardComparator cmptor = new CardComparator (); // 使用比较器对象进行比较 System . out . println ( cmptor . compare ( p , o )); // == 0 ,表示牌相等 System . out . println ( cmptor . compare ( p , q )); // < 0 ,表示 p 比较小 System . out . println ( cmptor . compare ( q , p )); // > 0 ,表示 q 比较大 } }
四 小结
覆写的方法 | 说明 |
Object.equals | 因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与 否 |
Comparable.compareTo | 需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于 内部顺序 |
Comparator.compare | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性 强 |
更新不易,希望大家多多支持,如有不当之处,不吝赐教!!!!!