List 排序
今天突然被同事问道:把list集合按照其中一个字段进行降序或者升序排列;
第一反应是可以在查SQL的时候用order by xxx desc(asc)就行了呀,实则不然,在做项目的时候有很多地方不仅仅是这样就行了
然后查了一些资料,有两种写法,自己在这里做一个记录:
首先看第一种 实现 Comparable 接口,实现 compareTo() 方法
最近用java抓取数据的时候,遇到了需要对拿到的List集合按照多个字段进行排序。
首先得让排序的实体类实现Comparable类并且重写它的compareTo方法
,在compareTo中你可以自定义排序的规则。
如果前者大于后者,则返回1,若果相等则返回0,若果前者小于后者,则返回-1;
String字符串比较的时候也会经常用到这个compareTo方法,查看String类,可以看到它也实现了Comparable类。
新建一个User类:按照id升序,age降序来排序。
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.Serializable;
@Data
@AllArgsConstructor
public class User implements Comparable<User> ,Serializable {
private static final long serialVersionUID = 7845798963273695832L;
private int id;
private int age;
private String name;
/*
* 按照id升序排列,按照age降序排列
* */
@Override
public int compareTo(User o) {
if (this.getId() > o.getId()) {
return 1;
} else if (this.getId() < o.getId()) {
return -1;
} else {
if (this.getAge() > o.getAge()) {
return -1;
} else {
return 1;
}
}
}
}
再建一个Junit测试类 Collections.sort(list);
@Test
public void test2(){
List<User> list = new ArrayList<>();
list.add(new User(1, 1, "A"));
list.add(new User(3, 2, "B"));
list.add(new User(3, 3, "C"));
list.add(new User(5, 4, "D"));
list.add(new User(5, 5, "E"));
System.out.println("排序前:"+list);
for (User user : list) {
System.out.println("id:"+user.getId()+" age:"+user.getAge()+" name:"+user.getName());
}
System.out.println("排序后:");
// 如果对象实现了 Comparable 接口并且重写了compareTo 方法,我们也可以使用这种方式进行排序
// list.stream().sorted().forEach(System.out::println);
//id升序,age降序
Collections.sort(list);
for (User user1 : list) {
System.out.println("id:"+user1.getId()+" age:"+user1.getAge()+" name:"+user1.getName());
}
}
排序结果:
id:1 age:1 name:A
id:3 age:2 name:B
id:3 age:3 name:C
id:5 age:4 name:D
id:5 age:5 name:E
排序后:
id:1 age:1 name:A
id:3 age:3 name:C
id:3 age:2 name:B
id:5 age:5 name:E
id:5 age:4 name:D
使用 Stream 流进行排序
@Data
@AllArgsConstructor
public class UserDto {
private int id;
private int age;
private String name;
public static void main(String[] args) {
List<UserDto> list = new ArrayList<>();
list.add(new UserDto(1, 1, "A"));
list.add(new UserDto(3, 5, "B"));
list.add(new UserDto(3, 3, "C"));
list.add(new UserDto(5, 4, "D"));
list.add(new UserDto(5, 5, "E"));
// 根据 id 升序排序
list.stream().sorted(Comparator.comparing(UserDto::getId)).forEach(System.out::println);
System.out.println("--------根据 age 降序排序----------------");
// 根据 age 降序排序 reversed就是将顺序倒过来
list.stream().sorted(Comparator.comparing(UserDto::getAge).reversed()).forEach(System.out::println);
// 根据 id 升序,如果id相同的话再根据 age 升序排序 (二级排序就是当一级排序的两个元素相等的时候,再用二级排序进行比较)
System.out.println("----根据 id 升序,如果id相同的话再根据 age 升序排序------");
list.stream().sorted(Comparator.comparing(UserDto::getId).thenComparing(UserDto::getAge))
.forEach(System.out::println);
/**
* 按照 id 升序排序, 按照 age 降序排序
*/
System.out.println("-----按照 id 升序排序, 按照 age 降序排序----");
list.stream().sorted((p1, p2) -> {
if (p1.getId() > p2.getId()) {
return 1;
} else if (p1.getId() < p1.getId()) {
return -1;
} else {
if (p1.getAge() > p2.getAge()) {
return -1;
} else {
return 1;
}
}
}).forEach(System.out::println);
}
}
第二种写法 实现 Comparator 接口,重写 Compare()方法
情景:现在有一个List,需要对病人每一次的体检记录(Quota)按照日期进行降序排序
(即近期放在最前面)
而Collections
的sort()
方法默认是升序
排列,如果需要降序排列时就需要重写compare方法
。
首先放出compare的注释
* @param o1 the first object to be compared.
* @param o2 the second object to be compared.
* @return a negative integer, zero, or a positive integer as the
* first argument is less than, equal to, or greater than the
* second.
* @throws NullPointerException if an argument is null and this
* comparator does not permit null arguments
* @throws ClassCastException if the arguments' types prevent them from
* being compared by this comparator.
*/
int compare(T o1, T o2);
也就是说默认o1和o2比较,如果o1-o2返回负数,则把o1放在o2前面
。(升序)
Collections.sort(list, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o1 - o2;//注意这一行代码
}
});
可我现在要实现降序,那么思路应该是
Collections.sort(list, new Comparator<Integer>() {
public int compare(Integer o1, Integer o2) {
return o2 - o1;//注意这一行代码
}
});
解释完compare方法,下面是该情景解决方案:
Quota实体类
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.io.Serializable;
@Data
@Slf4j
@AllArgsConstructor
public class Quota implements Serializable {
private static final long serialVersionUID = -3112288909486144393L;
private String date;
}
@Test
public void test3(){
List<Quota> list = new ArrayList<>();
list.add(new Quota("2020-07-01"));
list.add(new Quota("2020-07-09"));
list.add(new Quota("2020-05-06"));
list.add(new Quota("2020-06-06"));
list.add(new Quota("2020-03-07"));
//Collections的sort方法默认是升序排列,如果需要降序排列时就需要重写compare方法
Collections.sort(list, new Comparator<Quota>() {
@Override
public int compare(Quota o1, Quota o2) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
try {
//获取体检日期,并把其类型由String转化成Date,便于比较
Date dt1 = format.parse(o1.getDate());
Date dt2 = format.parse(o2.getDate());
//一下代码决定按日期降序排序,若将return ”-1“与"1"互换,即可实现降序
//getTime方法返回一个整数值,这个整数值代表了从1970年1月1日开始计算到date对象中的时间之间的毫秒数
if(dt1.getTime() > dt2.getTime()){
return -1;
}else if(dt1.getTime() < dt2.getTime()){
return 1;
}else {
return 0;
}
} catch (ParseException e) {
e.printStackTrace();
}
return 0;
}
});
for (Quota quota : list) {
System.out.println("date:"+quota.getDate());
}
}
执行结果
date:2020-07-09
date:2020-07-01
date:2020-06-06
date:2020-05-06
date:2020-03-07
上面第二种方法还有一种写法: list.sort();
list.sort(new Comparator<Quota>() {
@Override
public int compare(Quota o1, Quota o2) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
//获取体检日期,并把其类型由String转成Date,便于比较。
Date dt1 = format.parse(o1.getDate());
Date dt2 = format.parse(o2.getDate());
//以下代码决定按日期降序排序,若将return“-1”与“1”互换,即可实现升序。
//getTime 方法返回一个整数值,这个整数代表了从 1970 年 1 月 1 日开始计算到 Date 对象中的时间之间的毫秒数。
if (dt1.getTime() > dt2.getTime()) {
return -1;
} else if (dt1.getTime() < dt2.getTime()) {
return 1;
} else {
return 0;
}
}
});
注意:
一个是:Collections.sort(list);
另一个是:list.sort();