您的位置:首页 > 编程语言 > Java开发

Java集合框架(下)之Collection.sort()与Comparable,Comparator

2016-07-03 11:58 507 查看
在集合框架的应用中,往往都要进行排序,排序规则多种多样,或者只是简单的字符串大小排序,简单的数字大小排序,但有时根据需求排序规则会比较复杂。这时就需要Collection.sort()的使用技巧了。

对数字大小进行排序(Integer):

public void testSortInteger(){

List<Integer> list = new ArrayList<Integer>();
//往list中插入10个100以内的随机整数
Random random = new Random();
Integer k;
for(int i=0;i<10;i++){

do{//防止重复
k=random.nextInt(100);//自动装箱,且k在100之内
}while(list.contains(k));
list.add(k);
System.out.println("成功添加整数:"+k);
}
System.out.println("-----------排序前------------");
for(Integer integer:list){
System.out.println("元素:"+integer);
}
Collections.sort(list);//从小到大排序
System.out.println("-----------排序后------------");
for(Integer integer:list){
System.out.println("元素:"+integer);
}
}


运行结果:

成功添加整数:44

成功添加整数:28

成功添加整数:18

成功添加整数:56

成功添加整数:73

成功添加整数:84

成功添加整数:2

成功添加整数:54

成功添加整数:65

成功添加整数:53

———–排序前————

元素:44

元素:28

元素:18

元素:56

元素:73

元素:84

元素:2

元素:54

元素:65

元素:53

———–排序后————

元素:2

元素:18

元素:28

元素:44

元素:53

元素:54

元素:56

元素:65

元素:73

元素:84

结果分析:

从结果可以看到,对int的包装类Integer进行了从小到大的排序。

值得一提的是,程序中用了do…while循环排除重复的数字。while内是list.contains(k),就是一个个遍历元素与比较元素进行equals()比较,查看Integer的源码可以发现里面重写了equals()方法,就是比较拆箱后的值是否相等。

对字符串进行排序(String):

public void testSortString(){

List<String> list = new ArrayList<String>();
list.add("microsoft");
list.add("google");
list.add("Google");
list.add("h");
list.add("z");
System.out.println("-----------排序前------------");
for(String string:list){
System.out.println("元素:"+string);
}
Collections.sort(list);
System.out.println("-----------排序后------------");
for(String string:list){
System.out.println("元素:"+string);
}
//      排列顺序:
//      数字:0-9     48---57
//      大写字母:A-Z     65---90
//      小写字母:a-z     97---122
}


运行结果:

———–排序前————

元素:microsoft

元素:google

元素:Google

元素:h

元素:z

———–排序后————

元素:Google

元素:google

元素:h

元素:microsoft

元素:z

结果分析:

对字符串的排序,是一个个字符逐一比较,从小到大,跟字符串长度基本没有关系。从运行结果就能看出字符串的排序规则。

对普通对象的排序(Student):

public void testSortStudent(){

List<Student> list = new ArrayList<Student>();

Random random = new Random();
list.add(new Student(random.nextInt(1000)+"","小芳"));
list.add(new Student(random.nextInt(1000)+"","小明"));
list.add(new Student(random.nextInt(1000)+"","小红"));
list.add(new Student(10000+"","哈哈"));
System.out.println("-----------排序前------------");
for(Student student:list){
System.out.println("学生姓名:"+student.name+",学生id:"+student.id);
}
Collections.sort(list);
System.out.println("-----------排序后------------");
for(Student student:list){
System.out.println("学生姓名:"+student.name+",学生id:"+student.id);
}


如果这么写,编译器会报错,报错信息如下:

The method sort(List T) in the type Collections is not applicable for the arguments (List Student),意思是sort()方法不适用于对泛型为Student的List进行排序。为什么会这样呢?在API文档中详细查看对Collection.sort()的描述:

Sorts the specified list according to the order induced by the specified comparator. All elements in the list must be mutually comparable using the specified comparator (that is, c.compare(e1, e2) must not throw a ClassCastException for any elements e1 and e2 in the list).

即sort()方法操作的对象必须实现comparable接口。而我们的Student并不是comparable接口的实现类,所以报错。那为什么上面对Integer与String的排序就没有报错,来看看两者的源码:





可以看到,String与Integer都实现了Comparable接口。在各自的类定义中都重写了Comparable接口的compareTo()方法:

String的重写compareTo():

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;
}


Integer的重写compareTo():

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);
}


因此,实现了的comparable接口就是默认的比较规则。那我们要实现根据学生id大小(字符串)对学生进行排序,Student就要实现Comparable接口并重写compareTo()方法。

Student.java:

package com.imooc.collection;

import java.util.HashSet;
import java.util.Set;

public class Student implements Comparable<Student> {

public String id;
public String name;

public Set<Course> courses;
public Student(String id, String name) {
super();
this.id = id;
this.name = name;
this.courses = new HashSet<Course>();
}

@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}

@Override
public int compareTo(Student o) {
// TODO Auto-generated method stub
return this.id.compareTo(o.id);//规则:学生**id字符串**从小到大排序
//==0就是两者相等
//>0就是前者比后者大
//<0就是前者比后者小
}
}


这时编译正常并运行得到运行结果:

———–排序前————

学生姓名:小芳,学生id:734

学生姓名:小明,学生id:292

学生姓名:小红,学生id:549

学生姓名:哈哈,学生id:10000

———–排序后————

学生姓名:哈哈,学生id:10000

学生姓名:小明,学生id:292

学生姓名:小红,学生id:549

学生姓名:小芳,学生id:734

结果分析:

根据学生id(字符串)对学生进行了id从小到大排序。

如果想根据学生姓名来排序呢?显然可以在重写的compareTo()方法中定义根据学生姓名,而不是学生id的排序规则。下面就介绍Collection.sort()的另一种重载方法:

java.util.Collections.sort(List list, Comparator c)

新建StudentComparator.java:

package com.imooc.collection;

import java.util.Comparator;

public class StudentComparator implements Comparator<Student> {

@Override
public int compare(Student o1, Student o2) {
// TODO Auto-generated method stub
return o1.name.compareTo(o2.name);
}
}


实现Comparator接口,泛型为Student,重写compare方法,根据学生姓名定义临时排序规则。

运用重载的sort()方法:

public void testSortStudent(){

List<Student> list = new ArrayList<Student>();

Random random = new Random();
list.add(new Student(random.nextInt(1000)+"","Lucy"));
list.add(new Student(random.nextInt(1000)+"","Jack"));
list.add(new Student(random.nextInt(1000)+"","Hill"));
list.add(new Student(10000+"","0abc"));
System.out.println("-----------排序前------------");
for(Student student:list){
System.out.println("学生姓名:"+student.name+",学生id:"+student.id);
}
Collections.sort(list);
System.out.println("-----------排序后------------");
for(Student student:list){
System.out.println("学生姓名:"+student.name+",学生id:"+student.id);
}

Collections.sort(list, new StudentComparator());
System.out.println("-----------按照姓名排序后------------");
for(Student student:list){
System.out.println("学生姓名:"+student.name+",学生id:"+student.id);
}
}


运行结果:

———–排序前————

学生姓名:Lucy,学生id:591

学生姓名:Jack,学生id:650

学生姓名:Hill,学生id:140

学生姓名:0abc,学生id:10000

———–排序后————

学生姓名:0abc,学生id:10000

学生姓名:Hill,学生id:140

学生姓名:Lucy,学生id:591

学生姓名:Jack,学生id:650

———–按照姓名排序后————

学生姓名:0abc,学生id:10000

学生姓名:Hill,学生id:140

学生姓名:Jack,学生id:650

学生姓名:Lucy,学生id:591

结果分析:

按照姓名排序后的结果运用的是临时的比较器Comparator(通过StudentComparator实现)。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息