Comparable是排序接口。若一个类实现了Comparable接口,就意味着该类支持排序。实现了Comparable接口的类的对象的列表或数组可以通过Collections.sort或Arrays.sort进行自动排序。
public interface Comparable<T> { public int compareTo(T o); }
具体的使用看一个例子就明白,很简单
public class Person implements Comparable<Person>
{
String name;
int age;
public Person(String name, int age)
{
super();
this.name = name;
this.age = age;
}
public String getName()
{
return name;
}
public int getAge()
{
return age;
}
@Override
public int compareTo(Person p) //这里我们实现这个方法,并在里面添加比较的标准是什么
{
return this.age-p.getAge();
}
public static void main(String[] args)
{
Person[] people=new Person[]{new Person("xujian", 20),new Person("xiewei", 10)};
System.out.println("排序前");
for (Person person : people)
{
System.out.print(person.getName()+":"+person.getAge());
}
Arrays.sort(people);
System.out.println("\n排序后");
for (Person person : people)
{
System.out.print(person.getName()+":"+person.getAge());
}
}
}
关于这个比较的标准
@Override
public int compareTo(Person p)
{
return this.age-p.getAge();
}
意思是如果当前对象的age更大,那么返回一个正值,那么排序就是升序排序,如果我返回的是负值那么就是降序排序。为什么是这样呢,因为这里有一个规定:
compareTo返回1就会交换比较对象的位置,其他不会交换。
所以this.age-p.getAge();,如果当前对象age减去比较对象age大于0,然后交换当前对象到比较对象后面,说明就是升序。反之不交换就是降序
那么对于我们这个例子的解释,如果前一个对象的age大于比较对象的age,那么我们返回一个正值,符合升序的条件,所以这个排序的标准就是按照升序排序。如果改成
@Override
public int compareTo(Person p)
{
return -(this.age-p.getAge());
}
前一个对象的age大于比较对象的age,那么我们返回一个负值,满足的是降序的条件,那么排序的准则就是降序。
Comparator是比较接口,我们如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口),那么我们就可以建立一个“该类的比较器”来进行排序,这个“比较器”只需要实现Comparator接口即可。也就是说,我们可以通过实现Comparator来新建一个比较器,然后通过这个比较器对类进行排序。该接口定义如下:
package java.util; public interface Comparator<T> { int compare(T o1, T o2); boolean equals(Object obj); }
对于该接口的排序的准则上面已经说的很清楚了,一模一样。
举一个应用实例:
有一个英文的字符串,字符串有很多的单词,每个单词之间用空格隔开,就相当于是一个英文文章一样。那么这篇文章的单词有长有短,要求输出每一个单词,同时输出的顺序的按照单词的长度递增输出。首先肯定的是直接用split就可以将文章转换成一个字符串数组,那么剩下的就是对这个数组进行操作。直接用排序就ok,下面是一个最快捷的思路演示:
LinkedList<String> list =new LinkedList<>();
list.add("shi");
list.add("xiaoxiao");
list.add("xiao");
list.add("xiao");
list.add("shi");
list.add("xiaopang");
list.add("dapang");
list.add("haha");
Collections.sort(list,new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
// TODO Auto-generated method stub
return o1.length()-o2.length();
}
});
输出list:
shi
shi
haha
xiao
xiao
dapang
xiaopang
xiaoxiao //字符串长度递增
第二个实例,我们对一个二维数组进行排序,排序的标准是第二列。
String[][] arr={{"s", "2", "aaa"},{"r", "1" ,"bbb"},{"s" ,"0", "ccc"}};
Arrays.sort(arr, new Comparator<String[]>(){
@Override
public int compare(String[] o1, String[] o2) {
return o1[1].compareTo(o2[1]);
}
});
for(String[] s:arr){
System.out.println(Arrays.toString(s));
}
原先的数组是:
[s, 2, aaa]
[r, 1, bbb]
[s, 0, ccc]
排序结束之后为:
[s, 0, ccc]
[r, 1, bbb]
[s, 2, aaa]
临时表分组,排序,尽量用java8新特性stream进行处理
使用java8新特性,下面先来点基础的
List<类> list; 代表某集合
//返回 对象集合以类属性一升序排序
list.stream().sorted(Comparator.comparing(类::属性一));
//返回 对象集合以类属性一降序排序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed());//先以属性一升序,结果进行属性一降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()));//以属性一降序
//返回 对象集合以类属性一升序 属性二升序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二));
//返回 对象集合以类属性一降序 属性二升序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二));//先以属性一升序,升序结果进行属性一降序,再进行属性二升序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二));//先以属性一降序,再进行属性二升序
//返回 对象集合以类属性一降序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,升序结果进行属性一降序,再进行属性二降序
list.stream().sorted(Comparator.comparing(类::属性一,Comparator.reverseOrder()).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一降序,再进行属性二降序
//返回 对象集合以类属性一升序 属性二降序 注意两种写法
list.stream().sorted(Comparator.comparing(类::属性一).reversed().thenComparing(类::属性二).reversed());//先以属性一升序,升序结果进行属性一降序,再进行属性二升序,结果进行属性一降序属性二降序
list.stream().sorted(Comparator.comparing(类::属性一).thenComparing(类::属性二,Comparator.reverseOrder()));//先以属性一升序,再进行属性二降序
通过以上例子我们可以发现
-
Comparator.comparing(类::属性一).reversed();
-
Comparator.comparing(类::属性一,Comparator.reverseOrder());
两种排序是完全不一样的,一定要区分开来 1 是得到排序结果后再排序,2是直接进行排序,很多人会混淆导致理解出错,2更好理解,建议使用2
实际例子:
现有一个类test 有两个属性:state 状态 time 时间,需要状态顺序且时间倒序
//状态
private int state;
//时间
private Date time;
public test(int state, Date time) {
this.state = state;
this.time = time;
}
public int getState() {
return state;
}
public void setState(int state) {
this.state = state;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
@Override
public String toString() {
return "test{" +
"state=" + state +
", time=" + DateUtils.formatDateYMD(time) +
'}';
}
public static void main(String[] args) {
List<test> testList = new ArrayList<>();
Date d = DateUtils.now();
for (int i = 1; i <= 3; i++) {
test t = new test(i, DateUtils.addDays(d, i));
testList.add(t);
}
for (int i = 1; i <= 3; i++) {
test t = new test(i, DateUtils.addMonths(d, i));
testList.add(t);
}
testList.forEach(o -> {
System.out.println(o.toString());
});
List<test> sort = testList.stream().sorted(Comparator.comparing(test::getState).thenComparing(test::getTime,Comparator.reverseOrder())).collect(toList());
System.out.println("------------------------------------");
sort.forEach(o -> {
System.out.println(o.toString());
});
}