Comparable接口实现(当需要对某个对象进行排序时)
如标题所说,当你需要对某个自定义类进行排序时,你就需要实现Comparable接口。
反过来说,当一个类实现了Comparable接口时,就表明它的实例具有内在的排序关系。
Java平台类库中所有的值类(Integer, Short....),以及所有的枚举类型都实现了Comparable接口。
接下来我们先看一下应该怎么实现这个接口,以及应该怎么使用它:
类对象(第一种实现方式)
public class DemoComparable implements Comparable<DemoComparable>{
private int id;
private String name;
private int sort;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DemoComparable that = (DemoComparable) o;
return id == that.id &&
sort == that.sort &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, sort);
}
@Override
public String toString() {
return "DemoComparable{" +
"id=" + id +
", name='" + name + '\'' +
", sort=" + sort +
'}';
}
@Override
public int compareTo(DemoComparable o){
if (o == null){
throw new NullPointerException();
}
// 初始化result对象
int result = 0;
// 比较sort属性
result = Integer.compare(sort, o.getSort());
// 如果相同则继续比较下一属性,不相等则返回比较结果
if (result != 0){
return result;
}
// 比较id属性
result = Integer.compare(id, o.getId());
// 如果相同则继续比较下一属性,不相等则返回比较结果
if (result != 0){
return result;
}
return result;
}
}
数组排序
public class MainTest {
public static void main(String[] args) {
/**
* 生成对象的随机数组
*/
DemoComparable[] array = new DemoComparable[100];
for (int i = 0; i < 100; i++){
DemoComparable var = new DemoComparable();
// 获取随机数
Random random = new Random();
int rd = random.nextInt();
var.setId(rd);
var.setName("name"+rd);
var.setSort(rd);
array[i] = var;
}
// 排序
Arrays.sort(array);
// 输出结果
for (int i = 0; i < array.length; i++){
System.out.println(array[i].toString());
}
}
}
list排序
public class MainTest {
public static void main(String[] args) {
/**
* 生成对象的随机集合
*/
List<DemoComparable> array = new ArrayList<>();
for (int i = 0; i < 100; i++){
DemoComparable var = new DemoComparable();
// 获取随机数
Random random = new Random();
int rd = random.nextInt();
var.setId(rd);
var.setName("name"+rd);
var.setSort(rd);
array.add(var);
}
// 排序
Collections.sort(array);
// 输出结果
for (int i = 0; i < array.size(); i++){
System.out.println(array.get(i).toString());
}
}
}
当我们实现Comparable接口时要注意:
- 在compareTo方法中使用关系操作符 < 和 > 是非常繁琐的,并且容易出错,因此不建议使用。
- 如果一个类有多个属性需要进行排序,那么按照什么样的顺序来进行排序是非常关键的,我们需要从最关键的属性开始。
- compareTo方法的结果应该尽量与equals方法的结果保持一致,即当a.compareTo(b)等于0时,a和b的equals方法返回的结果应该是true。当然,如果他们不一致的话也不会产生灾难性的后果,但是如果在一个有序集合中包含了该类的元素,这个集合就可能无法遵守相应集合接口(Collection,Set,Map)的通用约定。因为这些接口的通用约定是按照equals方法来定义的,但有序集合使用了由compareTo方法而不是equals方法所施加的等同性测试。
接下来我们介绍一下对象类的第二种实现方式,性能损耗上要比第一种更大一些:
public class DemoComparable1 implements Comparable<DemoComparable1>{
/**
* 构建静态比较器,性能损耗相较于上一种方式大约10%
*/
private static final Comparator<DemoComparable1> COMPARATOR =
Comparator.comparingInt((DemoComparable1 dc) -> dc.id)
.thenComparingInt(dc -> dc.sort);
/**
* 比较方法实现
*/
@Override
public int compareTo(DemoComparable1 o) {
return COMPARATOR.compare(this, o);
}
private int id;
private String name;
private int sort;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getSort() {
return sort;
}
public void setSort(int sort) {
this.sort = sort;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
DemoComparable1 that = (DemoComparable1) o;
return id == that.id &&
sort == that.sort &&
Objects.equals(name, that.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, sort);
}
@Override
public String toString() {
return "DemoComparable1{" +
"id=" + id +
", name='" + name + '\'' +
", sort=" + sort +
'}';
}
}
其排序方式与第一种实现方式是一样的。
后一种方式中,Comparable类具备全套的构造方法。对于基本类型long和double都有对应的comparingInt和thenComparingInt。int版本也可以用更狭义的整数型类型,如Short。Double版本也可以用float。这样便覆盖了所有Java数字型基本类型。