在 Java
中我们知道基本数据类型,比如 int
、float
等可以使用 ==
、!=
、>
、<
来进行大小比较,但是对象或者包装类之间怎么进行比较呢,Java
也给我们提供了两个相关的类:Comparable
和 Comparator
public interface Comparable<T> {}
@FunctionalInterface
public interface Comparator<T> {}
那么它们两个有什么区别呢:
从命名上
从命名上,Comparable
是形容词,Comparator
是名词。可以理解为实现了 Comparable
接口的类具备了相互之间比较的能力;而 Comparator
的接口其中定义了一些默认的比较规则,还可以实现这个接口来自定义比较规则
从源码上
从源码上,Comparable
是一个接口,实现这个接口的类要重写其中定义的 compareTo()
方法;Comparator
是一个函数式接口,可以通过 lambda
表达式来使用
PriorityQueue
PriorityQueue
是一个优先级队列,其中的元素会按照它们的自然顺序排序(实现 Comparable
接口),或者由队列构造时提供的 Comparator
排序,两者必须存在一个。我们来看一下 PriorityQueue
的源码:
ublic class PriorityQueue<E> extends AbstractQueue<E>
implements java.io.Serializable {
// 自定义 Comparator,由构造方法传入
private final Comparator<? super E> comparator;
public PriorityQueue() {
this(DEFAULT_INITIAL_CAPACITY, null);
}
public PriorityQueue(Comparator<? super E> comparator) {
this(DEFAULT_INITIAL_CAPACITY, comparator);
}
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;
}
public boolean add(E e) {
return offer(e);
}
public boolean offer(E e) {
if (e == null)
throw new NullPointerException();
modCount++;
int i = size;
if (i >= queue.length)
grow(i + 1);
size = i + 1;
if (i == 0)
queue[0] = e;
else
// 添加的时候排序
siftUp(i, e);
return true;
}
private void siftUp(int k, E x) {
if (comparator != null)
// 构造方法传入的 Comparator
siftUpUsingComparator(k, x);
else
// 使用类自身的 Comparable 能力
siftUpComparable(k, x);
}
private void siftUpComparable(int k, E x) {
// 如果类没有实现 Comparable 接口,这里会抛出 ClassCastException 类型转化异常
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)
// 如果要添加进来的值,大于等于 e,则不做任何操作
break;
queue[k] = e;
k = parent;
}
queue[k] = key;
}
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;
}
}
接下来就通过 PriorityQueue
来理解 Comparable
和 Comparator
这两个概念。
Comparable
要理解 Comparable
,我们首先来看一下 Java
中 Integer
类的源码:
public final class Integer extends Number implements Comparable<Integer> {
...
public int compareTo(Integer anotherInteger) {
return compare(this.value, anotherInteger.value);
}
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
...
}
可以看到 Integer
类实现了 Comparable
接口,并且重写了 compareTo()
方法,说明 Integer
类具有了比较的能力。compareTo()
方法返回值是一个 int
类型的数字,为 -1
表示传入的对象大,为 0
表示一样大,1
表示 this
对象大。
使用 PriorityQueue
测试 Integer
中 Comparable
的测试用例:
void testComparable() {
// Integer 实现了 Comparable,所以这里可以使用无参构造器
Queue<Integer> queue = new PriorityQueue<>();
queue.add(76);
queue.add(1);
queue.add(8);
queue.add(4);
queue.add(88);
queue.add(54);
System.out.println(queue.element()); // 1
}
Comparator
首先定义一个 People
类,只有一个年龄 age
属性:
@Data
@AllArgsConstructor
public class People {
private Integer age;
}
定义一个 Comparator
:
public class MyComparator implements Comparator<People> {
@Override
public int compare(People o1, People o2) {
// 返回正整数表示 o1 > o2
return o1.getAge() - o2.getAge();
}
}
测试用例:
void testComparator() {
// 这里一定要传入自定义的 Comparator
Queue<People> queue = new PriorityQueue<>(new MyComparator());
queue.add(new People(21));
queue.add(new People(10));
queue.add(new People(44));
queue.add(new People(56));
queue.add(new People(35)); // People(age=10)
System.out.println(queue.element());
}
对于 Comparable
和 Comparator
的应用还是非常简单的,从上面两个示例中基本就能理解,所以就不做过多的解释了,无非是要分清楚谁大谁小而已:
对于实现 Comparable
接口的 compareTo()
方法来说,返回值为正表示传入的对象大,为负表示传入的对象小于 this
对象。
对于实现 Comparator
接口的 compare()
方法来说,返回值为正表示传入的第一个对象大于第二个对象,为负表示第一个对象小于第二个对象。