java对象的比较
1. PriorityQueue中插入对象
优先级队列在插入元素时有个要求:插入的元素不能是null或者元素之间必须要能够进行比较,那如果我们插入的是自定义类型对象该如何比较呢?
比如说我们定义了一个学生类
class Student{
public int age;
public String name;
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "ahangsan";
student1.age = 10;
Student student2 = new Student();
student2.name = "zhangsan";
student2.age = 9;
}
}
要想将这两个对象放入优先级队列中,直接放是不可能的,通过PriorityQueue的offer方法的原码可知,如果没有comparator,对象会被强转成Comparable类型,所以我们可以有两种方式:
一是构建comparator;
二是实现Comparable接口。
2.对象的比较
- 基于Comparble接口类的比较
源码如下:
public interface Comparable<E> {
// 返回值:
// < 0: 表示 this 指向的对象小于 o 指向的对象
// == 0: 表示 this 指向的对象等于 o 指向的对象
// > 0: 表示 this 指向的对象大于 o 指向的对象
int compareTo(E o);
}
对用用户自定义类型,如果要想按照大小与方式进行比较时:在定义类时,实现Comparble
接口即可,然后在类中重写compareTo
方法。
class Student implements Comparable<Student> {
public int age;
public String name;
@Override
public int compareTo(Student o) {
//比较对象的大小关系,返回只有正负数和0
return this.age - o.age;
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "ahangsan";
student1.age = 10;
Student student2 = new Student();
student2.name = "zhangsan";
student2.age = 9;
System.out.println(student1.compareTo(student2));
}
}
上面的代码可以根据年龄来进行比较,但是这种方法有个弊端,如果我想根据姓名进行比较,我还要改写我的compareTo
方法,这样一来,我原先使用年龄比较的代码就不能用了。
2. 覆写基类的equals
class Student {
public int age;
public String name;
@Override
public boolean equals(Object o) {
//判断是否相等,返回true或者false
if (this == o) return true;//判断是否是自己和自己比较
if (o == null || getClass() != o.getClass()) return false;//判断对象是否为空,或者o是不是Student的子类
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);//判断年龄,姓名是否一样
//注意基本类型可以直接比较,但引用类型最好调用其equal方法
}
}
public class Test {
public static void main(String[] args) {
Student student1 = new Student();
student1.name = "ahangsan";
student1.age = 10;
Student student2 = new Student();
student2.name = "zhangsan";
student2.age = 9;
Student student2 = new Student();
student2.name = "zhangsan";
student2.age = 9;
System.out.println(student1.equals(student2));
System.out.println(student3.equals(student2));
}
}
输出结果是:
false
true
这个方法只能用作对象之间的比较,比较是否相等,而不能比较出谁大谁小,所以不能用在PriorityQueue
的插入元素中。
如果没有重写equals
方法,那会自动调用object类的equals
方法,此时和直接写student1 == student2
没有区别,比较的还是对象的地址。
覆写基类equal的方式虽然可以比较,但缺陷是:equal只能按照相等进行比较,不能按照大于、小于的方式进行比较
- 基于比较器比较
- 用户自定义比较器类,实现Comparator接口
public interface Comparator<T> {
// 返回值:
// < 0: 表示 o1 指向的对象小于 o2 指向的对象
// == 0: 表示 o1 指向的对象等于 o2 指向的对象
// > 0: 表示 o1 指向的对象等于 o2 指向的对象
int compare(T o1, T o2);
}
注意区分Comparator和Comparable
- 覆写Comparator中的compare方法
class Student {
public int age;
public String name;
}
}
//年龄比较器
class AgeComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.age - o2.age;
}
}
//姓名比较器
class NameComparator implements Comparator<Student> {
@Override
public int compare(Student o1, Student o2) {
return o1.name.compareTo(o2.name);//调用引用类型String的方法
}
}
public class Test {
public static void main(String[] args[]) {
Student student1 = new Student();
student1.name = "zhangsan";
student1.age = 10;
Student student2 = new Student();
student2.name = "zhangsan";
student2.age = 9;
AgeComparator ageComparator = new AgeComparator();
System.out.println(ageComparator.compare(student1,student2));
NameComparator nameComparator = new NameComparator();
System.out.println(nameComparator.compare(student1,student2));
}
}
输出:
1 // student1年龄大于student2
0 // student1姓名和student2相同
三种方法的对比
覆写的方法 | 说明 |
---|---|
Object.equals | 因为所有类都是继承自 Object 的,所以直接覆写即可,不过只能比较相等与否 |
Comparable.compareTo | 需要手动实现接口,侵入性比较强,但一旦实现,每次用该类都有顺序,属于内部顺序 |
Comparator.compare | 需要实现一个比较器对象,对待比较类的侵入性弱,但对算法代码实现侵入性强 |
3. PriorityQueue的比较方式
我们了解了对象的比较方式,现在就回过来思考PriorityQueue内部的元素是如何采用Comparble和Comparator比较的呢?
// JDK中PriorityQueue的实现:
public class PriorityQueue<E> extends AbstractQueue<E> implements java.io.Serializable {
// ...
// 默认容量
private static final int DEFAULT_INITIAL_CAPACITY = 11;
// 内部定义的比较器对象,用来接收用户实例化PriorityQueue对象时提供的比较器对象
private final 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;
}
}
-
Comparble是默认的内部比较方式,如果用户插入自定义类型对象时,该类对象必须要实现Comparble接口,并覆写compareTo方法。
class Student implements Comparable<Student> { public int age; public String name; @Override public int compareTo(Student o) { //比较对象的大小关系,返回只有正负数和0 return this.age - o.age; } } public class Test { public static void main(String[] args) { Student student1 = new Student(); student1.name = "ahangsan"; student1.age = 10; Student student2 = new Student(); student2.name = "zhangsan"; student2.age = 9; PriorityQueue<Student> priorityQueue = new PriorityQueue<>(); priorityQueue.offer(student1); priorityQueue.offer(student2); Student ret = priorityQueue.poll(); //ret = "zhangsan" ,9 } }
-
用户也可以选择使用比较器对象,如果用户插入自定义类型对象时,必须要提供一个比较器类,让该类实现Comparator接口,并覆写compare方法
class Student implements Comparable<Student> { public int age; public String name; @Override public int compareTo(Student o) { //比较对象的大小关系,返回只有正负数和0 return this.age - o.age; } } class AgeComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.age - o2.age; } } class NameComparator implements Comparator<Student> { @Override public int compare(Student o1, Student o2) { return o1.name.compareTo(o2.name); } } public class Test { public static void main(String[] args) { Student student1 = new Student(); student1.name = "ahangsan"; student1.age = 10; Student student2 = new Student(); student2.name = "zhangsan"; student2.age = 9; AgeComparator ageComparator = new AgeComparator(); PriorityQueue<Student> priorityQueue1 = new PriorityQueue<>(ageComparator); //PriorityQueue<Student> priorityQueue1 = new PriorityQueue<>(new AgeComparator()); priorityQueue1.offer(student1); priorityQueue1.offer(student2); System.out.println(priorityQueue1.poll()); //"zhangsan" ,9 NameComparator nameComparator = new NameComparator(); PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(nameComparator); //PriorityQueue<Student> priorityQueue2 = new PriorityQueue<>(new NameComparator()); priorityQueue2.offer(student1); priorityQueue2.offer(student2); System.out.println(priorityQueue2.poll()); //"ahangsan" ,10 } }
这两种方式可以同时存在,互不影响。