在各种场景中,对集合进行排序是个十分常见的操作,我们很自然地想到用集合工具类Collections
中的sort
方法直接集合进行排序操作。但是我们在使用的过程中会发现,如果集合的泛型是Integer
、String
这种JDK自带的类型的时候,可以直接使用这个sort方法,但是如果我们想对自定义的泛型集合使用时,会报错。以自定义的User
类为例:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int age;
private String name;
}
IDE会提示我们没有实现Comparable
接口,也就是本文要讲的内容之一
上面提到我们可以对Integer
、String
这种JDK自带的类型直接使用Collections.sort()
方法对集合进行排序。这是因为这些常用的类型JDK都帮我们实现了Comparable
接口。以Integer
为例,看下它的声明:
public final class Integer extends Number implements Comparable<Integer> {
...}
好的,现在我们来看下Comparable
接口到底长啥样:
public interface Comparable<T> {
public int compareTo(T o);
}
可以看到该接口相当简单,只有一个需要重写的compareTo
方法,该方法上有一段注释是这么写的:
Compares this object with the specified object for order. Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
这段话的意思是用这个对象的值,也就是this
去跟另一个对象(也就是传进来的参数o
)的值进行比较,如果比它小就返回负数,如果和它相等就返回0,如果比它大就返回正数。
我翻了很多博客发现对这句话的解释就到这里结束了,没有讲到最关键的东西,那就是像上面所说的方式返回值的话,集合中元素的排序会根据比较的那个字段的进行升序排序。那如果我们想要降序排序,只需要跟上面反着来就行了,如果比它小就返回正数,如果和它相等就返回0,如果比它大就返回负数。
我们来看下Integer
中是如何实现这个方法的:
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);
}
可以看到Integer
中对接口方法的重写完全遵循了升序排序的规则。所以直接使用Collections.sort()
可以对集合中的元素进行升序排序。
综上,我们想要对自己定义的类使用Collections.sort()
进行排序,只需要令这个类实现Comparable
接口,然后根据我们想要的排序方式,实现其中的compareTo
方法就行了。
下面来个简单的使用示例,对本文一开始给出的User
类的集合进行排序:
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Comparable<User>{
private int age;
private String name;
@Override
public int compareTo(User user) {
return this.getAge() - user.getAge();
}
}
List<User> list = new ArrayList<>();
list.add(new User(2,"name22"));
list.add(new User(3,"name333"));
list.add((new User(1,"name1")));
Collections.sort(list);
System.out.println(list);//[User(age=1, name=name1), User(age=2, name=name2), User(age=3, name=name3)]
那么问题来了,我们经常会使用到别人写的类,别人写的时候没有实现Comparable
接口,有时候我们也不太好去修改别人的代码,那该怎么办呢?这时候就要用到Comparator
接口了!
我们直接来看Comparator
接口定义:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
...
}
Comparator
接口被标记为@FunctionalInterface
函数式接口,包含众多默认方法、静态方法和一个抽象方法compare
,我们这里只关注compare(T o1, T o2)
这个方法。
Comparator
接口的排序逻辑和Comparable
接口一致,只需要把o1
理解为this
主动比较的对象,把o2
理解为o
被比较的对象即可。
使用Collections.sort()
的重载形式sort(List
将需要排序的集合和Comparator
传进去即可。(这边涉及到的函数式接口和Lambda表达式的相关知识就不在本文讲解了)
下面看下使用示例(集合还是上面的集合不变):
Collections.sort(list,((o1, o2) -> {
if(o1.getName().length()<o2.getName().length()){
return 1;
}else {
return -1;
}
}));
System.out.println(list);//[User(age=3, name=name333), User(age=2, name=name22), User(age=1, name=name1)]
示例代码中,我使用了降序的逻辑对名字的长度进行了排序。需要注意的是,上述代码中,我没有取消Comparable
接口的实现,所以说,在使用这种方式进行排序时,排序的逻辑会覆盖Comparable
接口中的排序逻辑。
以上就是对Comparable与Comparator的通俗讲解!