https://www.cnblogs.com/ldy-blogs/p/8488138.html
在Java中经常会涉及到对象数组的排序问题,那么就涉及到对象之间的比较问题。
在Arrays.sort()和Collections.sort()中
通常对象之间的比较可以从两个方面去看:
第一个方面:对象的地址是否一样,也就是是否引用自同一个对象。
这种方式可以直接使用“==“来完成。
第二个方面:以对象的某一个属性的角度去比较。
对于JDK8而言,有三种实现对象比较的方法:
1、覆写Object类的equals()方法;
2、继承Comparable接口,并实现compareTo()方法;
3、定义一个单独的对象比较器,继承自Comparator接口,实现compare()方法。
由于使用的排序方式的不同,具体选择哪种方法来实现对象的比较也会有所不同。
Comparable 简介
Comparable 是排序接口。
Comparable可以认为是一个内比较器
若一个类实现了Comparable接口,就意味着“该类支持排序”。 即然实现Comparable接口的类支持排序,假设现在存在“实现Comparable接口的类的对象的List列表(或数组)”,则该List列表(或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。
此外,“实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
Comparable 定义
Comparable 接口仅仅只包括一个函数,它的定义如下:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
说明:
假设我们通过 x.compareTo(y) 来“比较x和y的大小”。
若返回“负数”,意味着“x比y小”;
返回“零”,意味着“x等于y”;
返回“正数”,意味着“x大于y”。
我们采用Arrays.sort()和Collections.sort() 这个方法完成的排序时,实际上要求了被排序的类型
需要实现Comparable接口,完成比较的功能
我们一般见到的类型 都继承了Comparable接口,如:
public final class Integer extends Number implements Comparable<Integer> {..}
public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {...}
但是这样就把这种规则写死了,比如我想要字符串按照第一个字符降序排列,那么这样就要修改String的源代码,这是不可能的了,那么这个时候我们可以使用
public static <T> void sort(List<T> list,Comparator<? super T> )
方法灵活的完成 。
Comparator 简介
Comparator 是比较器接口。
Comparator可以认为是是一个外比较器
我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么,我们可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator接口即可。
也就是说,我们可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序
Comparator 定义
Comparator 接口仅仅只包括两个个函数,它的定义如下:
package java.util;
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
说明:
(01) 若一个类要实现Comparator接口:
它一定要实现compareTo(T o1, T o2) 函数,但可以不实现equals(Object obj) 函数。
为什么可以不实现 equals(Object obj) 函数呢?
因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。
(02) int compare(T o1, T o2) 是“比较o1和o2的大小”。
返回“负数”,意味着“o1比o2小”;
返回“零”,意味着“o1等于o2”;
返回“正数”,意味着“o1大于o2”。
Comparable和Comparator的比较
不同博主的总结
"Comparator"和“Comparable”的比较程序。
(01) "Comparable"
* 它是一个排序接口,只包含一个函数compareTo()。
* 一个类实现了Comparable接口,就意味着“该类本身支持排序”,它可以直接通过Arrays.sort() 或 Collections.sort()进行排序。
(02) "Comparator"
* 它是一个比较器接口,包括两个函数:compare() 和 equals()。
* 一个类实现了Comparator接口,那么它就是一个“比较器”。其它的类,可以根据该比较器去排序。
*
综上所述:Comparable是内部比较器,而Comparator是外部比较器。
一个类本身实现了Comparable比较器,就意味着它本身支持排序;
若它本身没实现Comparable,也可以通过外部比较器Comparator进行排序。
------------------------------------------------------------------------------------
两种方法各有优劣, 用Comparable 简单, 只要实现Comparable 接口的对象直接就成为一个可以比较的对象,但是需要修改源代码。
用Comparator 的好处是不需要修改源代码, 而是另外实现一个比较器, 当某个自定义的对象需要作比较的时候,把比较器和对象一起传递过去就可以比大小了, 并且在Comparator 里面用户可以自己实现复杂的可以通用的逻辑,使其可以匹配一些比较简单的对象,那样就可以节省很多重复劳动了。
----------------------------------------------------------------------------
总结一下,两种比较器Comparable和Comparator,后者相比前者有如下优点:
1、如果实现类没有实现Comparable接口,又想对两个类进行比较(或者实现类实现了Comparable接口,但是对compareTo方法内的比较算法不满意),那么可以实现Comparator接口,自定义一个比较器,写比较算法
2、实现Comparable接口的方式比实现Comparator接口的耦合性 要强一些,如果要修改比较算法,要修改Comparable接口的实现类,而实现Comparator的类是在外部进行比较的,不需要对实现类有任何修改。从这个角度说,其实有些不太好,尤其在我们将实现类的.class文件打成一个.jar文件提供给开发者使用的时候。实际上实现Comparator 接口的方式后面会写到就是一种典型的策略模式。
当然,这不是鼓励用Comparator,意思是开发者还是要在具体场景下选择最合适的那种比较器而已。
例子
(1)在List集合中的数字,升序输出一遍,降序输出一遍
public static void main(String[] args) {
List<Integer> list=new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);//[2,1,3]
//默认升序
Collections.sort(list);//默认升序
for (Integer integer : list) {
System.out.print(integer);//123
}
System.out.println();
//降序
Collections.sort(list,new Comparator<Integer>(){
@Override
public int compare(Integer o1, Integer o2) {//注意:写法
return o2-o1;
}
});
for (Integer integer : list) {
System.out.print(integer);//321
}
}
-----------
123
321
(2)自定义Student类,有name和id,按照age降序输出(用两种写法)
一、在类中重写Comparable的compareTo方法
//student类
public class Student implements Comparable<Student> {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
//Override重写比较方法
@Override
public int compareTo(Student o) {
return o.age-this.age;//降序
}
}
//测试
public class TestStudent {
public static void main(String[] args) {
List<Student> list=new ArrayList<>();
list.add(new Student("rose",3));
list.add(new Student("jack",2));
list.add(new Student("abc",1));
list.add(new Student("ace",5));
list.add(new Student("mark",4));
//按age的降序排列
Collections.sort(list);
for (Student s : list) {
System.out.println(s);
}
}
}
----------------------------------
Student{name='ace', age=5}
Student{name='mark', age=4}
Student{name='rose', age=3}
Student{name='jack', age=2}
Student{name='abc', age=1}
(2)用Collections.sort(List list,Comparetor c)方式
//Strudent类
public class Student {
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
//测试
public class TestStudent {
public static void main(String[] args) {
List<Student> list=new ArrayList<>();
list.add(new Student("rose",3));
list.add(new Student("jack",2));
list.add(new Student("abc",1));
list.add(new Student("ace",5));
list.add(new Student("mark",4));
//按age的降序排列
Collections.sort(list, new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge()-o1.getAge();
}
});
for (Student s : list) {
System.out.println(s);
}
}
}
----------------------------------
Student{name='ace', age=5}
Student{name='mark', age=4}
Student{name='rose', age=3}
Student{name='jack', age=2}
Student{name='abc', age=1}