1、进行List排序的元素,必须实现Comparable接口。
像List<Integer>、List<String>我们可能会有这样的感觉: 我并没有做任何操作,直接Collections.sort() 就可以排序啊。
其实那是因为Integer、String早已经实现了Comparable接口,我们看下Integer、String的源码:
public final class Integer extends Number implements Comparable<Integer> {
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
2、看下Integer、String是根据什么进行排序的,也就是实现的compareTo方法,这对我们进行自定义对象排序有很好的借鉴作用。
我们先看下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);
}
它的compareTo方法中调用自己的compare方法,其实就是比较大小来排序。我们再看下String类型的:
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
return c1 - c2;
}
k++;
}
return len1 - len2;
}
它先拿到要比较的两个Sting的长度,然后取两个长度中最小的一个,然后循环两个char数组,如果两个char不相等,则比较结束。只有前面元素都一致导致比较不出来结果时再根据长度来比较,所以并不是一般认为的长度越长肯定排序越靠后。比如'A'的ASCII码是65,'a'的ASCII码是97,所以如果一个是A开头的字符串,一个是a开头的字符串,不管长度多少,sort排序后肯定是a开头的字符串排在后面。举例说明下:
List<String> list = new ArrayList<String>();
list.add("aa");
list.add("AAA");
list.add("bb");
list.add("ab");
list.add("abb");
Collections.sort(list);
System.out.println(list);
最后排序结果是: [AAA, aa, ab, abb, bb]
3、我们一般用到的都是List保存的是自定义对象,或者是Map结构,通过实现Comparable接口来排序。
我们有一个Student类,里面有4个属性: id、name、age、score,分别表示学号、姓名、年龄、综合评分。我们有一个这样的需求: 先按照学生的年龄排序再根据评分排序,我们看如何实现。
首先我们要把类实现Comparable接口,然后重写compareTo方法,代码如下:
public class Student implements Comparable<Student>{
private Integer id;
private String name;
private Integer age;
private Integer score;
public Student(Integer id, String name, Integer age, Integer score){
this.id = id;
this.name = name;
this.age = age;
this.score = score;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getScore() {
return score;
}
public void setScore(Integer score) {
this.score = score;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", age=" + age
+ ", score=" + score + "]";
}
@Override
public int compareTo(Student o) {
int i = this.getAge() - o.getAge(); // 先根据年龄排序
if(i == 0){
return this.score - o.getScore(); //年龄相当则根据评分排序
}
return i;
}
}
然后写一个测试类来验证:
public class TestStudent {
public static void main(String[] args) {
Student s1 = new Student(1, "张三", 20, 89);
Student s2 = new Student(2, "李四", 21, 97);
Student s3 = new Student(3, "王五", 20, 70);
Student s4 = new Student(4, "赵六", 21, 85);
List<Student> list = new ArrayList<Student>();
list.add(s3);
list.add(s1);
list.add(s2);
list.add(s4);
Collections.sort(list);
System.out.println(list);
}
}
排序结果:
[Student [id=3, name=王五, age=20, score=70],
Student [id=1, name=张三, age=20, score=89],
Student [id=4, name=赵六, age=21, score=85],
Student [id=2, name=李四, age=21, score=97]]
这种排序一般被我们称为自然排序,下面我们说另一种实现方式,一般称为定制排序。
4、通过匿名内部类,实现Comparator接口的类来实现排序,这种实现方式一般称为定制排序。
这种方式一般更适合List<Map>这种结构,当然像List<Student>这种也可以用这种方式实现。实际项目中比如我们通过接口获取了一部分数据,接口返回结果是Json结构,我们把相应的数据转为List<Map>这种结构,但是接口返回的数据并不是按照我们想要的顺序来排序的,需要我们再次排序,这个时候我们就需要用到定制排序了。
Collections.sort(askList, new Comparator<Map<String, Object>>(){
public int compare(Map<String, Object> map1, Map<String, Object> map2) {
long d1 = Long.parseLong(map1.get("updateTime").toString());
long d2 = Long.parseLong(map2.get("updateTime").toString());
if((d1- d2) < 0){
return 1;
}
if((d1- d2) == 0){
return 0;
}
return -1;
}
});
askList就是返回接口的List<Map>集合,我们希望按照里面的一个字段updateTime(最后更新时间)来排序。
希望对你有帮助,欢迎批评指正讨论。欢迎关注我的微信公众号:javaSharing。