排序自定义对象的数组
假设我们有这样一个student的类
Student s2 = new Student(3,“李四”,18);
Student s3 = new Student(2,“王五”,50);
Student s4 = new Student(1,“赵六”,38);
第一列对应为ID,第二列对应为名字,第三列对应为年龄。
至于如何创建这个类不在我们的讨论范围。
现在我们想针对Student中的年龄进行按从小到大的排序应该如何做到呢?
通过JDK的API手册中Arrays.sort我们可以发现这样这两个重载的方法。
static void sort(Object[] a)
根据元素的自然顺序对指定对象数组按升序进行排序。
static <T> void sort(T[] a, Comparator<? super T> c)
根据指定比较器产生的顺序对指定对象数组进行排序。
很明显这两个数组都是对自定义对象的数组进行排序的。
通过进一步查询API
自然排序
sort
public static void sort(Object[] a)
根据元素的自然顺序对指定对象数组按升序进行排序。数组中的所有元素都必须实现 Comparable 接口。此外,数组中的所有元素都必须是可相互比较的(也就是说,对于数组中的任何 e1 和 e2 元素而言,e1.compareTo(e2) 不得抛出 ClassCastException)。
保证此排序是稳定的:不会因调用 sort 方法而对相等的元素进行重新排序。
该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n*log(n) 性能。
参数:
a - 要排序的数组
抛出:
ClassCastException - 如果数组包含不可相互比较的 的元素(例如,字符串和整数)。
上面提到了 Comparable接口。查询手册里面就只有一种方法
int compareTo(T o) 比较此对象与指定对象的顺序。
也就是说,我们需要对自定义对象数组的类实现(implements)这个接口。接口类型为Comparable<T>。
啊~~~
通过API手册我们对自然排序得到了以下的结论:
-
对自定义对象调用接口Comparable
-
然后再对象数组中重写int compareTo(T o)方法
-
然后就放心的交给Arrays.sort排序吧。
以下是完整代码:
import java.util.Arrays;
public class PolyDemo04 {
public static void main(String[] args) {
Student s1 = new Student("张三",25);
Student s2 = new Student("李四",18);
Student s3 = new Student("王五",50);
Student s4 = new Student("赵六",38);
Student[] stu = {s1,s2,s3,s4};
System.out.println(Arrays.toString(stu));
System.out.println("=======================================");
// 针对对象数组进行排序
Arrays.sort(stu);
System.out.println(Arrays.toString(stu));
}
}
class Student implements Comparable<Student> {
//以下的代码为构建Student,可以略过直接到最后一行
private int ID;
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public int getID() {
return ID;
}
public void setID(int iD) {
ID = iD;
}
public Student() {
super();
}
public Student(int ID,String name, int age) {
super();
this.ID = ID;
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Student [name=" + name + ", age=" + age + "]";
}
//到此为止
@Override
public int compareTo(Student o) {
return o.getAge() - this.age;
}
}
其实上面一大半的代码都是构建get/set方法 真正的代码只有
public int compareTo(Student o) {
return o.getAge() - this.age;
}
来看看运行的效果吧
排序前
[Student [name=张三, age=25], Student [name=李四, age=18], Student [name=王五, age=50], Student [name=赵六, age=38]]
排序后
[Student [name=王五, age=50], Student [name=赵六, age=38], Student [name=张三, age=25], Student [name=李四, age=18]]
啊~我们看到都按照年龄排序了。
关于排序方法的探究
让我们在深入一点看看我们的sort到底用了那种方法来排序。代码很简单只需要在compareTo里面加入三行测试代码
@Override
public int compareTo(Student o) {
System.out.println("this===" + this);
System.out.println(" o===" + o);
System.out.println("============================");
return o.getAge() - this.age;
}
this***Student [name=李四, age=18]
o***Student [name=张三, age=25]
this***Student [name=王五, age=50]
o***Student [name=李四, age=18]
this***Student [name=王五, age=50]
o***Student [name=李四, age=18]
this***Student [name=王五, age=50]
o***Student [name=张三, age=25]
this***Student [name=赵六, age=38]
o***Student [name=张三, age=25]
this***Student [name=赵六, age=38]
o***Student [name=王五, age=50]
上面的排序是一个二分法的插入排序。具体探究过程再次篇幅有限就不介绍了。
定制排序
上面这个排序很好,但是对于行业潜规则Java bean来说显然在student类里面去重写一个借口显得惨无人道。之后维护代码的时候想要换一种排序还要回来找这个Student类。
那么有没有一种排序可以单独在一个类中就完成我们的分类呢?
在上面提到了还有一种排序
以下信息可以直接跳
sort
public static void sort(T[] a,Comparator<? super T> c)
根据指定比较器产生的顺序对指定对象数组进行排序。数组中的所有元素都必须是通过指定比较器可相互比较的(也就是说,对于数组中的任何 e1 和 e2 元素而言,c.compare(e1, e2) 不得抛出 ClassCastException)。
保证此排序是稳定的:不会因调用 sort 方法而对相等的元素进行重新排序。
该排序算法是一个经过修改的合并排序算法(其中,如果低子列表中的最高元素小于高子列表中的最低元素,则忽略合并)。此算法提供可保证的 n*log(n) 性能。
参数:
a - 要排序的数组
c - 确定数组顺序的比较器。null 值指示应该使用元素的自然顺序。
抛出:
ClassCastException - 如果数组包含使用指定的比较器不可相互比较的 的元素。
再来看看
Comparator<? super T> c
看看方法:int compare(T o1, T o2)
比较用来排序的两个参数。
显然这里意思让我们重写他了
箱子让我们看看步骤
- 创建一个类去实现这个接口啦
- 与上面雷同
开始码代码吧
import java.util.Comparator;
class ComparatorImpl implements Comparator<Student>{
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
return result = o1.getAge() - o2.getAge();
}
}
其他的类和之前都一样喽。
但是我们在排序前要new一个ComparatorImpl类调用他的方法先
Comparator<Student> ci = new ComparatorImpl();
Arrays.sort(stu,ci);
但是仅仅就这样就完了吗?
很多童鞋想到
Student s1 = new Student(4,“张三”,25);
Student s2 = new Student(3,“李四”,25);
Student s3 = new Student(2,“王五”,25);
Student s4 = new Student(1,“赵六”,38);
要是年龄一样怎么办呢?是不是要按照ID的顺序来排呢?
那么我们再来看看重写的compare
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
return result = o1.getAge() - o2.getAge();
}
}
我们看到return的就是一个int还是两个年龄相减后的!!那么要是两个年龄相等我们是不是只用把ID减一下就好了呢?
@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
int result = o1.getAge() - o2.getAge();
if (result == 0) {
result = o1.getId() - o2.getId();
}
return result;
}
大功告成
看一下输出的代码
[Student [id=2, name=王五, age=25],
Student [id=3, name=李四, age=25],
Student [id=4, name=张三, age=25],
Student [id=1, name=赵六, age=38]]
好像成功了呢!
总结
还要总结!! 多照着码几遍吧。