集合排序涉及到的接口Comparable and Comparator

Comparable and Comparator

一、Comparable

1. API解释(源码翻译神器电梯直达

  此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
  实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射中的键或有序集合中的元素,无需指定比较器。
  对于类 C 的每一个 e1 和 e2 来说,当且仅当 e1.compareTo(e2) == 0 与 e1.equals(e2) 具有相同的 boolean 值时,类 C 的自然排序才叫做与 equals 一致。注意,null 不是任何类的实例,即使 e.equals(null) 返回 false,e.compareTo(null) 也将抛出 NullPointerException。
  建议(虽然不是必需的)最好使自然排序与 equals 一致。这是因为在使用自然排序与 equals 不一致的元素(或键)时,没有显式比较器的有序集合(和有序映射表)行为表现“怪异”。尤其是,这样的有序集合(或有序映射表)违背了根据 equals 方法定义的集合(或映射表)的常规协定。
  例如,如果将两个键 a 和 b 添加到没有使用显式比较器的有序集合中,使 (!a.equals(b) && a.compareTo(b) == 0),那么第二个 add 操作将返回 false(有序集合的大小没有增加),因为从有序集合的角度来看,a 和 b 是相等的。

  实际上,所有实现 Comparable 的 Java 核心类都具有与 equals 一致的自然排序。java.math.BigDecimal 是个例外,它的自然排序将值相等但精确度不同的 BigDecimal 对象(比如 4.0 和 4.00)视为相等。
  从数学上讲,定义给定类 C 上自然排序的关系式 如下:
    {(x, y)|x.compareTo(y) <= 0}。
  整体排序的 商 是:
    {(x, y)|x.compareTo(y) == 0}。
  它直接遵循 compareTo 的协定,商是 C 的 等价关系,自然排序是 C 的 整体排序。当说到类的自然排序 与 equals 一致 时,是指自然排序的商是由类的 equals(Object) 方法定义的等价关系。
{(x, y)|x.equals(y)}。

2. 使用方法(在1中提取)

  int compareTo(T o)
  比较此对象与指定对象的顺序。如果该对象小于、等于或大于指定对象,则分别返回负整数、零或正整数。
  参数: o - 要比较的对象。
  利用当前对象和传入的目标对象进行比较:
    若是当前对象比目标对象大,则返回1,那么当前对象会排在目标对象的后面
    若是当前对象比目标对象小,则返回-1,那么当前对象会排在目标对象的后面
    若是两个对象相等,则返回0

  抛出:ClassCastException - 如果指定对象的类型不允许它与此对象进行比较。

3. 示例:

  • Student类实现Comparable接口,重写CompareTo方法
public class Student implements Comparable<Student>{
    private long id;
    private String name;
    private int age;
    public Student(Long id,String name,int age){
        this.id=id;
        this.name=name;
        this.age=age;
    }
    @Override
    public String toString() {
        return "Student{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
    @Override
    public int compareTo(Student o) {
        if(this.age>o.age){
            return -1;//由高到底排序
        } else if(this.age<o.age){
            return 1;
        }else {
            if(this.id>o.id){
                return 1;//由底到高排序
            }else if(this.id<o.id){
                return -1;
            }else{
                return 0;
            }
        }
    }
}
  • 测试代码
 @Test
    public void testComparable(){
        Student student1=new Student(100001l,"lqh",18);
        Student student2=new Student(100004l,"Jack Ma",21);
        Student student3=new Student(100003l,"lqd",21);
        Student student4=new Student(100002l,"MaHuaTeng",19);
        Student students[]={student1,student2,student3,student4};

        System.out.println("********************排序前*********************");
        for(Student s:students){
            System.out.println(s);
        }
        System.out.println("***************array********************");
        Arrays.sort(students);
        for(Student s:students){
            System.out.println(s);
        }
        Student students1[]={student1,student2,student3,student4};
        List<Student> list=Arrays.asList(students1);

        System.out.println();
        System.out.println("********************排序前*********************");
        for(Student s:list){
            System.out.println(s);
        }
        System.out.println("***************list(有序的,可以重复的)********************");
        Collections.sort(list);
        for(Student s:list){
            System.out.println(s);
        }

        System.out.println();
        Student students2[]={student1,student2,student3,student4};
        System.out.println("**************set(无序的,不可重复的)********************");
        System.out.println("使用自动排序的容器(TreeSet、TreeMap)");
        Set<Student> set1=new TreeSet<>(Arrays.asList(students2));
        for(Student s:set1){
            System.out.println(s);
        }
    }
  • 输出结果
********************排序前*********************
Student{id=100001, name='lqh', age=18}
Student{id=100004, name='Jack Ma', age=21}
Student{id=100003, name='lqd', age=21}
Student{id=100002, name='MaHuaTeng', age=19}
***************array********************
Student{id=100003, name='lqd', age=21}
Student{id=100004, name='Jack Ma', age=21}
Student{id=100002, name='MaHuaTeng', age=19}
Student{id=100001, name='lqh', age=18}

********************排序前*********************
Student{id=100001, name='lqh', age=18}
Student{id=100004, name='Jack Ma', age=21}
Student{id=100003, name='lqd', age=21}
Student{id=100002, name='MaHuaTeng', age=19}
***************list(有序的,可以重复的)********************
Student{id=100003, name='lqd', age=21}
Student{id=100004, name='Jack Ma', age=21}
Student{id=100002, name='MaHuaTeng', age=19}
Student{id=100001, name='lqh', age=18}

二、Comparator

1. API解释

  强行对某个对象 collection 进行整体排序 的比较函数。可以将 Comparator 传递给 sort 方法(如 Collections.sort 或 Arrays.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如有序 set或有序映射)的顺序,或者为那些没有自然顺序的对象 collection 提供排序。
  当且仅当对于一组元素 S 中的每个 e1 和 e2 而言,c.compare(e1, e2)==0 与 e1.equals(e2) 具有相等的布尔值时,Comparator c 强行对 S 进行的排序才叫做与 equals 一致 的排序。
  当使用具有与 equals 不一致的强行排序能力的 Comparator 对有序 set(或有序映射)进行排序时,应该小心谨慎。假定一个带显式 Comparator c 的有序 set(或有序映射)与从 set S 中抽取出来的元素(或键)一起使用。如果 c 强行对 S 进行的排序是与 equals 不一致的,那么有序 set(或有序映射)将是行为“怪异的”。尤其是有序 set(或有序映射)将违背根据 equals 所定义的 set(或映射)的常规协定。
  例如,假定使用 Comparator c 将满足 (a.equals(b) && c.compare(a, b) != 0) 的两个元素 a 和 b 添加到一个空 TreeSet 中,则第二个 add 操作将返回 true(树 set 的大小将会增加),因为从树 set 的角度来看,a 和 b 是不相等的,即使这与 Set.add 方法的规范相反。
注:通常来说,让 Comparator 也实现 java.io.Serializable 是一个好主意,因为它们在可序列化的数据结构(像 TreeSet、TreeMap)中可用作排序方法。为了成功地序列化数据结构,Comparator(如果已提供)必须实现 Serializable。
  在算术上,定义给定 Comparator c 对给定对象 set S 实施强行排序 的关系式 为:
     {(x, y) such that c.compare(x, y) <= 0}.
此整体排序的 商 (quotient) 为:
     {(x, y) such that c.compare(x, y) == 0}.
  它直接遵循 compare 的协定,商是 S 上的 等价关系,强行排序是 S 上的 整体排序。当我们说 c 强行对 S 的排序是 与 equals 一致 的时,意思是说排序的商是对象的 equals(Object) 方法所定义的等价关系:
     {(x, y) such that x.equals(y)}.

2. 使用方法

compare(T o1, T o2):
   如果返回的值小于零,则不交换两个o1和o2的位置;
   如果返回的值大于零,则交换o1和o2的位置。
注意,不论在compare(o1, o2)中是如何实现的(第一种实现方式是 o1-02, 第二种实现方式是 o2 - o1),都遵循上述原则,即返回值小于零,则交换o1和o2的位置;返回值大于零,则不交换o1和o2的位置。 所以,如果采用第一种实现方式,即 o1 - o2, 那么将是升序排序。因为在原始排序中o1在o2的前边,如果o1小于o2,那么o1 - o2小于零,即返回值是小于零,但是小于零是不会交换o1和o2的位置的,所以o1依然排在o2的前边,是升序;如果o1大于o2,那么o1 - o2大于零,即返回值是大于零,大于零是要交换o1和o2的位置的,所以要改变原始排序中o1和o2的位置,那么依然是升序

3. 示例

  • Dog类
public class Dog {
    private long id;
    private String name;
    private int age;
    //此处省略getter和setter方法
    public Dog(Long id, String name, int age){
        this.id=id;
        this.name=name;
        this.age=age;
    }
    @Override
    public String toString() {
        return "Dog{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
  • 测试方法
 @Test
    public void testComparator(){
        Dog dog=new Dog(20001l,"大黄",4);
        Dog dog1=new Dog(20003l,"肉丝",3);
        Dog dog2=new Dog(20002l,"翠花",5);
        Dog dogs[]={dog,dog1,dog2};
        System.out.println("***************排序前*****************");
        for(Dog d:dogs){
            System.out.println(d);
        }
        Arrays.sort(dogs, new Comparator<Dog>() {
            @Override
            public int compare(Dog o1, Dog o2) {
                if(o1.getAge()>o2.getAge()){
                    return -1;//由高到底排序
                } else if(o1.getAge()<o2.getAge()){
                    return 1;
                }else {
                    if(o1.getId()>o2.getId()){
                        return 1;//由底到高排序
                    }else if(o1.getId()<o2.getId()){
                        return -1;
                    }else{
                        return 0;
                    }
                }
            }
        });
        System.out.println("***************排序后*****************");
        for(Dog d:dogs){
            System.out.println(d);
        }
        System.out.println();
        Dog dogs1[]={dog,dog1,dog2};
        List<Dog> list=Arrays.asList(dogs1);
        System.out.println("***************排序前*****************");
        for(Dog d:list){
            System.out.println(d);
        }
        Collections.sort(list, new Comparator<Dog>() {
            @Override
            public int compare(Dog o1, Dog o2) {
                if(o1.getAge()>o2.getAge()){
                    return 1;
                } else if(o1.getAge()<o2.getAge()){
                    return -1;
                }else {
                    if(o1.getName().hashCode()>o2.getName().hashCode()){
                        return -1;
                    }else if(o1.getName().hashCode()<o2.getName().hashCode()){
                        return 1;
                    }else{
                        return 0;
                    }
                }
            }
        });
        System.out.println("***************排序后*****************");
        for(Dog d:list){
            System.out.println(d);
        }
    }
  • 输出结果
***************排序前*****************
Dog{id=20001, name='大黄', age=4}
Dog{id=20003, name='肉丝', age=3}
Dog{id=20002, name='翠花', age=5}
***************排序后*****************
Dog{id=20002, name='翠花', age=5}
Dog{id=20001, name='大黄', age=4}
Dog{id=20003, name='肉丝', age=3}

***************排序前*****************
Dog{id=20001, name='大黄', age=4}
Dog{id=20003, name='肉丝', age=3}
Dog{id=20002, name='翠花', age=5}
***************排序后*****************
Dog{id=20003, name='肉丝', age=3}
Dog{id=20001, name='大黄', age=4}
Dog{id=20002, name='翠花', age=5}

三、总结

  • Comparator是java.util包下提供的接口,而Comparable是java.lang包提供的接口
  • 都是Java中的内部比较器接口,都是用来实现对一个自定义的类进行排序
  • comparable是需要比较的对象来实现接口。这样对象调用实现的方法来比较。对对象的耦合度高(需要改变对象的内部结构,破坏性大)。comparator相当于一通用的比较工具类接口。需要定制一个比较类去实现它,重写里面的compare方法,方法的参数即是需要比较的对象。对象不用做任何改变。解耦。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值