背景:
最近定位了一个list集合排序乱序问题,算是比较典型的问题,在此分享一下。
问题描述
使用集合中Collections.sort()
方法对list集合排序
时,排序的结果中出现了乱序问题
,问题代码如下:
CarRecord .java:
package com.sk.bean;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class CarRecord implements Comparable<CarRecord> {
private Integer id;
private String plateCode;
private Long passTime;
@Override
public int compareTo(CarRecord o) {
return (int) (o.passTime - this.passTime);
}
}
Test.java
package com.sk.test;
import com.sk.bean.CarRecord;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
public class Test01 {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
List<CarRecord> list = new ArrayList<>();
list.add(new CarRecord(1, "鲁A 12345", 1688634366000L));
list.add(new CarRecord(2, "鲁A 12346", 1688568125000L));
list.add(new CarRecord(3, "鲁A 12347", 1688504221000L));
list.add(new CarRecord(4, "鲁A 12348", 1686144663000L));
list.add(new CarRecord(5, "鲁A 12349", 1686094000000L));
list.add(new CarRecord(5, "鲁A 12349", 1686043339000L));
list.add(new CarRecord(5, "鲁A 12349", 1685992410000L));
Collections.sort(list);
list.stream().forEach(x -> {
System.out.println(x.toString() + " 时间:" + sdf.format(new Date(x.getPassTime())));
});
}
}
执行结果:
CarRecord(id=4, plateCode=鲁A 12348, passTime=1686144663000) 时间:2023-06-07 21:31:03
CarRecord(id=5, plateCode=鲁A 12349, passTime=1686094000000) 时间:2023-06-07 07:26:40
CarRecord(id=5, plateCode=鲁A 12349, passTime=1686043339000) 时间:2023-06-06 17:22:19
CarRecord(id=5, plateCode=鲁A 12349, passTime=1685992410000) 时间:2023-06-06 03:13:30
CarRecord(id=1, plateCode=鲁A 12345, passTime=1688634366000) 时间:2023-07-06 17:06:06
CarRecord(id=2, plateCode=鲁A 12346, passTime=1688568125000) 时间:2023-07-05 22:42:05
CarRecord(id=3, plateCode=鲁A 12347, passTime=1688504221000) 时间:2023-07-05 04:57:01
我们按照passTime
字段进行排序,通过观察执行结果能够明显发现数据的passTime
字段是存在乱序的。
问题原因:
通过分析代码发现问题出现在代码(int) (o.passTime - this.passTime)
上,我们知道java中long和int类型的数值长度返回是不一样的,时间o.passTime - this.passTime差值的范围超过int数值范围时,强制转int类型会造成数据丢失;
注:
**int**
的取值范围为(-2147483648~2147483647
),占用4个字节(-2的31次方到2的31次方-1) ;
**long**
的取值范围为(-9223372036854774808~9223372036854774807
),占用8个字节(-2的63次方到2的63次方-1);
id3的passtime-id4的passtime值:1688504221000-1686144663000=2359558000 > 2147483647
由上可知,相同年月的passtime之间的差值在int数值时间范围内,所以相同年月的数据是有序的。
问题解决:
代码修改:(int) (o.passTime - this.passTime)
修改成compare(o.passTime, this.passTime)
,即:
Collections.sort(list, new Comparator<Long>() {
@Override
public int compare(Long l1, Long l2) {
return Long.compare(l1,l2);
}
});
查看compare()
方法的源码,如下:
public static int compare(long x, long y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
修改后的执行结果:
CarRecord(id=1, plateCode=鲁A 12345, passTime=1688634366000) 时间:2023-07-06 17:06:06
CarRecord(id=2, plateCode=鲁A 12346, passTime=1688568125000) 时间:2023-07-05 22:42:05
CarRecord(id=3, plateCode=鲁A 12347, passTime=1688504221000) 时间:2023-07-05 04:57:01
CarRecord(id=4, plateCode=鲁A 12348, passTime=1686144663000) 时间:2023-06-07 21:31:03
CarRecord(id=5, plateCode=鲁A 12349, passTime=1686094000000) 时间:2023-06-07 07:26:40
CarRecord(id=5, plateCode=鲁A 12349, passTime=1686043339000) 时间:2023-06-06 17:22:19
CarRecord(id=5, plateCode=鲁A 12349, passTime=1685992410000) 时间:2023-06-06 03:13:30
阿里《java开发手册》中对Collections.sort()规范要求: