上期我们简单的介绍了下java中的排序接口Comparable的使用,由于Comparable是不够灵活的一种排序接口,它要去修改对象的类的代码。所以,我们今天介绍一款较为灵活的比较器接口Comparator。
首先,还是创建一个实体类Person2:
public class Person2 {
private int age;
private String name;
private int xuehao;
public Person2() {
}
public Person2(int age, String name, int xuehao) {
this.age = age;
this.name = name;
this.xuehao = xuehao;
}
public int getXuehao() {
return xuehao;
}
public void setXuehao(int xuehao) {
this.xuehao = xuehao;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person2{" +
"age=" + age +
", name='" + name + '\'' +
", xuehao=" + xuehao +
'}';
}
}
然后在我们的创作一个测试类Mytest:
public class Mytest {
public static void main(String[] args) {
Person2[] person2s = {new Person2(15,"张三",34),
new Person2(5, "李四",12),
new Person2(10, "王五",88)};
//创建比较器对象cp
Comparator<Person2> cp = new Comparator<Person2>() {
@Override//重写compare方法,使按照xuehao升序
//o2.getXuehao()-o1.getXuehao():按照xuehao降序
public int compare(Person2 o1, Person2 o2) {
return o1.getXuehao()-o2.getXuehao();
}
};
System.out.println("排序前:");
for(Person2 p:person2s){
System.out.println(p.toString());
}
//对数组person2s安装比较器cp进行排序
Arrays.sort(person2s, cp);
System.out.println("排序后:");
for(Person2 p:person2s){
System.out.println(p.toString());
}
}
}
运行结果如下:(按照xuehao升序)
排序前:
Person2{age=15, name='张三', xuehao=34}
Person2{age=5, name='李四', xuehao=12}
Person2{age=10, name='王五', xuehao=88}
排序后:
Person2{age=5, name='李四', xuehao=12}
Person2{age=15, name='张三', xuehao=34}
Person2{age=10, name='王五', xuehao=88}
当然,你也可以修改比较器中的compare方法,使其安装age降序,具体修改如下:
//创建比较器对象cp
Comparator<Person2> cp = new Comparator<Person2>() {
@Override//重写compare方法,使按照age降序
public int compare(Person2 o1, Person2 o2) {
//若为o1.getAge()-o2.getAge():按照age升序
return o2.getAge()-o1.getAge();
}
};
修改后,运行结果如下:
排序前:
Person2{age=15, name='张三', xuehao=34}
Person2{age=5, name='李四', xuehao=12}
Person2{age=10, name='王五', xuehao=88}
排序后:
Person2{age=15, name='张三', xuehao=34}
Person2{age=10, name='王五', xuehao=88}
Person2{age=5, name='李四', xuehao=12}
可以发现我们修改person2的排序方式并不用去修改person2中的代码,所以说Comparator比Comparable更加的灵活。
最后我们看下Comparator在优先队列中的使用,我们修改Mytest中的代码如下:
public class Mytest {
public static void main(String[] args) {
//创建一个比较器
Comparator<Person2> cp = new Comparator<Person2>() {
@Override
public int compare(Person2 o1, Person2 o2) {
//安照年龄升序
return o1.getAge()-o2.getAge();
}
};
//将比较器cp作为参数创建一个优先队列
PriorityQueue<Person2> priorityQueue = new PriorityQueue<>(cp);
//往队列中加入数据
priorityQueue.add(new Person2(15,"张三",2));
priorityQueue.add(new Person2(33,"张三",78));
priorityQueue.add(new Person2(1,"张三",33));
priorityQueue.add(new Person2(5, "李四",14));
priorityQueue.add(new Person2(55,"张三",35));
priorityQueue.add(new Person2(10, "王五",22));
System.out.println("遍历优先队列:");
for(Person2 p:priorityQueue){
System.out.println(p.toString());
}
System.out.println("依次弹出优先队列中的值");
while(!priorityQueue.isEmpty()){
System.out.println(priorityQueue.poll());
}
}
}
运行结果如下:
遍历优先队列:
Person2{age=1, name='张三', xuehao=33}
Person2{age=5, name='李四', xuehao=14}
Person2{age=10, name='王五', xuehao=22}
Person2{age=33, name='张三', xuehao=78}
Person2{age=55, name='张三', xuehao=35}
Person2{age=15, name='张三', xuehao=2}
依次弹出优先队列中的值
Person2{age=1, name='张三', xuehao=33}
Person2{age=5, name='李四', xuehao=14}
Person2{age=10, name='王五', xuehao=22}
Person2{age=15, name='张三', xuehao=2}
Person2{age=33, name='张三', xuehao=78}
Person2{age=55, name='张三', xuehao=35}
显然,重结果中我们可以看出,优先队列弹出元素的顺序是按照age升序的。同时,细心的同学可以从我们的遍历结果中发现,优先队列中各个元素是按照其年龄(age)值来维护了一个完全二叉树的小顶堆,如下图所示:(节点中为各个元素age的值)
下面我们去修改比较器,具体代码如下:
//创建一个比较器
Comparator<Person2> cp = new Comparator<Person2>() {
@Override
public int compare(Person2 o1, Person2 o2) {
//按照xuehao降序
return o2.getXuehao()-o1.getXuehao();
}
};
然后再运行,得到如下结果:
遍历优先队列:
Person2{age=33, name='张三', xuehao=78}
Person2{age=55, name='张三', xuehao=35}
Person2{age=1, name='张三', xuehao=33}
Person2{age=15, name='张三', xuehao=2}
Person2{age=5, name='李四', xuehao=14}
Person2{age=10, name='王五', xuehao=22}
依次弹出优先队列中的值
Person2{age=33, name='张三', xuehao=78}
Person2{age=55, name='张三', xuehao=35}
Person2{age=1, name='张三', xuehao=33}
Person2{age=10, name='王五', xuehao=22}
Person2{age=5, name='李四', xuehao=14}
Person2{age=15, name='张三', xuehao=2}
此时我们可以发现我们元素的弹出顺序是按照xuehao降序的,而遍历的结果是按照xuehao来维护的一个大顶堆(如下图 )。
至此,我们可以总结为如下:
在优先队列中,如果定义的比较器是按照某值X升序(return o1.X-o2.X)排序的,那么优先队列中元素的位置会按照X的值所构建的小顶堆来放置;
在优先队列中,如果定义的比较器是按照某值X降序(return o2.X-o1.X)排序的,那么优先队列中元素的位置会按照X的值所构建的大顶堆来放置;
在使用比较器的时候,你也可以先创建一个比较器的类,然后在要使用的时候去new一个该类的对象也可以。比如在我们例子中,我们可以去创建下面这个类:
public class MyComparator implements Comparator<Person2> {
@Override
public int compare(Person2 o1, Person2 o2) {
//按照年龄age升序
return o1.getAge()-o2.getAge();
}
}
然后在使用的时候直接去创建该类的对象,可以将我们的测试类的代码修改如下部分:
//创建一个比较器
// Comparator<Person2> cp = new Comparator<Person2>() {
// @Override
// public int compare(Person2 o1, Person2 o2) {
// //按照xuehao降序
// return o2.getXuehao()-o1.getXuehao();
// }
// };
//此处比较器的排序规则要到MyComparator中去查看或修改
MyComparator cp = new MyComparator();
比较器接口Comparator的介绍到此就暂告一段落了,若文中有描述不当之处,希望大家多多指出,我们一起学习一起进步。