目录
PriorityQueue中插入对象
元素的比较
基本类型的比较
对象比较问题
对象的比较
覆写基类的equals
基于Comparble接口类的比较
基于比较器的比较
三种方式的对比
集合框架中PriorityQueue的比较方式
PriorityQueue中插入对象
上一篇博文中我们讲了优先级队列,优先级队列插入元素时有个要求:插入的元素不能是null或者元素之间必须能够进行比较,为了简单起见,我们只是插入了Integer类型,那优先级队列中能否插入自定义类型对象呢?
来看下面这个例子:
class Student {public int age;//年龄public String name;//姓名public Student(int age, String name) {this.age = age;this.name = name;}
}public class TestPriorityQueue {public static void TestPriorityQueue(){PriorityQueue<Student> p = new PriorityQueue<>();p.offer(new Student(18, "lisi"));p.offer(new Student(6, "wangwu"));}public static void main(String[] args) {TestPriorityQueue();}
}
你会发现这样的运行结果:
出现这种情况是因为优先队列底层使用堆,而向堆中插入元素时,为了满足堆的性质,必须要进行元素的比较,而此时Student是没有办法直接进行比较的,因此抛出异常。
元素的比较
基本类型的比较
在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);}
}
对象比较问题
class Student {public int age;//年龄public String name;//姓名public Student(int age, String name) {this.age = age;this.name = name;}
}public class TestPriorityQueue {public static void main(String[] args) {Student s1 = new Student(18, "lisi");Student s2 = new Student(6, "wangwu");Student s3 = s1;//System.out.println(s1 > s2); 编译报错System.out.println(s1 == s2);//编译成功->打印false,因为它们指向的是不同对象//System.out.println(s1 < s2); 编译报错System.out.println(s1 == s2);//编译成功->打印true,因为s1和s3指向的是同一对象}
}
从编译结果可以看出,Java中引用类型的变量不能直接按照>或者<方式进行比较。那为什么==可以比较?
那是因为:对于用户实现自定义类型,都默认继承自Object类,而Object类中提供了equal方法,而==默认情况下调用的就是equal方法,但是该方法的比较规则是:没有比较引用变量引用对象的内容,而是直接比较引用变量的地址,但有些情况下该种比较就不符合题意。
//Object中equal的实现,可以看到:直接比较的是两个引用变量的地址
public boolean equals(Object obj) {return (this == obj);
}
对象的比较
有些情况下,需要比较的是对象的内容,比如:向优先级队列中插入某个对象时,需要对按照对象中的内容来调整堆,那该如何处理呢?有以下几种方法:
覆写基类的equals
代码如下:
class Student {public int age;//年龄public String name;//姓名public Student(int age, String name) {this.age = age;this.name = name;}}@Overridepublic boolean equals(Object o) {//自己和自己比较if(this == o) {return true;//指向一个对象则返回true}//o如果是null对象,或者o不是Student的子类if(o == null || !(o instanceof Student)) {return false;}//注意基本类型可以直接比较,但引用类型最好调用其equal方法Student c = (Student)o;return age == c.age && name.equals(c.name);}
注意:一般覆写equals的套路如上
1.如果指向同一个对象,返回true
2.如果传入的是null,返回false
3.如果传入的对象类型不是Student, 返回false
4.按照类的实现目标完成比较,例如这里只要年龄和姓名一样,就认为是相同的学生
5.注意下调调用其它的引用类型的比较也需要equals,例如这里的name比较
覆写基类equals的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于,小于的方式进行比较。
基于Comparble接口类的比较
Comparble是JDK提供的泛型比较接口类,源码实现具体如下:
public interface Comparable<E> {//返回值//<0:表示this指向的对象小于o指向的对象//==0:表示this指向的对象等于o指向的对象//>0:表示this指向的对象大于o指向的对象int compareTo(E o);
}
对用用户自定义类型,如果想要按照大小与方式进行比较时:在定义类时,实现Comparble接口即可,然后在类中重写compareTo方法。
使用举例:
public class Student implements Comparable<Student> {public int age;public String name;public Student(int age, String name) {this.age = age;this.name = name;}//根据年龄比较,不管姓名//这里我们认为null是最小的@Overridepublic int compareTo(Student o) {if(o == null) {return 1;}return age - o.age;}public static void main(String[] args) {Student s1 = new Student(18, "lisi");Student s2 = new Student(6, "wangwu");Student s3 = new Student(18, "wangwu");System.out.println(s1.compareTo(s3));//==0System.out.println(s1.compareTo(s2));//>0System.out.println(s2.compareTo(s1));//<0}
}
Comparable是java.lang中的接口类,可以直接使用。
基于比较器的比较
按照比较器方式进行比较,具体步骤如下:
用户自定义比较器类,实现Comparator接口
public interface Comparator<T> {//返回值//<0:表示o1指向的对象小于o2指向的对象//==0:表示o1指向的对象等于o2指向的对象//>0:表示o1指向的对象大于o2指向的对象int compare(T o1, T o2);
}
注意:区分Comparable和Comparator.
覆写Comparator中的compare方法
代码如下:
class Student {public int age;//年龄public String name;//姓名public Student(int age, String name) {this.age = age;this.name = name;}
}public class StudentComparator implements Comparator<Student> {//根据年龄比较,不管姓名//这里我们认为null是最小的@Overridepublic int compare(Student o1, Student o2) {if(o1 == o2) {return 0;}if(o1 == null) {return -1;}if(o2 == null) {return 1;}return o1.age - o2.age;}public static void main(String[] args) {Student p = new Student(18, "lisi");Student q = new Student(6, "wangwu");Student o = new Student(18, "wangwu");//定义比较器对象StudentComparator comparator = new StudentComparator();//使用比较器对象进行比较System.out.println(comparator.compare(p, o));System.out.println(comparator.compare(p, q));System.out.println(comparator.compare(q, p));}
}
注意:Comparator是java.util包中的泛型接口类,使用时必须导入对应的包
三种方式的对比
覆写的方法 | 说明 |
Object.equals | 因为所有类都是继承自Object的,所以直接覆写即可,不过只能比较相等与否 |
Comparable.compareTo | 需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序 |
Comparator.compare | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强 |
集合框架中PriorityQueue的比较方式
集合框架中的PriorityQueue底层使用堆结构,因此其内部的元素必须要能够比较大小,Priority采用了:Comparble和Comparator两种方式。
1.Comparable是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparable接口,并覆写compareTo方法。
2.用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口并覆写compare方法。