【Java】深入理解Comparable和Comparator对象排序

Arrays和Collections工具类

Java为我们提供了实用的操作数组和集合的工具类,Arrays和Collections。内含对数组或集合的各种排序方法,数组与集合的转换方法。

> Arrays.sort

Arrays.sort()有两个重要的重载方法,一个是直接传入要排序的数组,另一个是传入数组和comparator比较器。
在进行对象排序时,默认调用TimSort,TimSort是归并排序的优化版本一种比MergeSort更高效的排序方式。

> Collections.sort

Collections.sort()与Arrays.sort()传参基本相同。
collections中的数据在排序前需要输入到array中,接着调用Arrays.sort函数来完成对象排序。

关于详细的排序实现,以后单独写文章细讲。

Comparable与Comparator的区别

Comparable和Comparator都是用来实现集合中元素的比较、排序的。
Comparable是在集合内部定义的方法实现的排序,位于java.util下。Comparator是在集合外部实现的排序,位于java.lang下。
Comparable是一个对象本身就已经支持自比较所需要实现的接口,如String、Integer自己就实现了Comparable接口,可完成比较大小操作。自定义类要在加入list容器中后能够排序,也可以实现Comparable接口,在用Collections类的sort方法排序时若不指定Comparator,那就以自然顺序排序。所谓自然顺序就是实现Comparable接口设定的排序方式。
Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足要求时,可写一个比较器来完成两个对象之间大小的比较。Comparator体现了一种策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
总而言之Comparable是自已完成比较,Comparator是外部程序实现比较
引用:https://juejin.cn/post/6844903603258261518

源码中对Comparable接口的实现

先看源码中对Comparable接口作出的定义:

该接口对每个类的对象施加一个总的排序实现它,这种顺序被称为类的自然排序,类的比较方法(CompareTo)称为其自然比较方法。
实现此接口的List集合(或数组)可以由Collections.sort和Arrays.sort自动排序。实现此接口的对象可以作为SortedMap中的键或SortedSet中的元素使用,而不需要指定Comparator。

官方解释的比较清楚。对于不清楚如何排序的对象,需要该对象的类使用Comparable接口来指定排序的方法(如果该类不实现Comparable接口,也可以指定一个Comparator进行排序,这点一会说)。 随后,就可以使用Arrays.sort或Collections.sort进行排序了。

看一个源码对其实现的简单的例子:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        String[] arr = {"E", "A", "D", "B", "C"};
        Arrays.sort(arr);
        for (String str : arr) {
            System.out.println(str);
        }
    }
}

输出结果:A B C D E

String类是对象,在这里我们没有对String类做什么操作,只是赋值,为什么String类能实现自动排序?我们深入到底层源码看一下
在这里插入图片描述
实现的CompareTo方法:
在这里插入图片描述
可以看到,之所以String自动完成了排序,是String类在设计的时候实现了Comparable接口,重写了compareTo方法。这样,在调用Arrays.sort时,对象会参考重写的compareTo方法进行排序。

使用Comparable接口完成第一个对象排序实例

String类的Comparable接口使用了泛型,通过查看Comparable接口的源码可以看到,这里的泛型规定了Object类的具体子类,一般传入实现该接口的类即可,比如Student类,泛型传入Student。

package java.lang;
import java.util.*;

public interface Comparable<T> {
    public int compareTo(T o);
}

参考String类的设计就可以使用Comparable接口自己实现对象排序了。
值得注意的是,若Comparable接口不使用泛型,在重写compareTo方法时需要向下转型。

Student类:

public class Student implements Comparable<Student> {
   String name;
   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
   public int compareTo(Student o) {
       if(this.age == o.age) {
           return this.name.compareTo(o.name);
       } else {
           return this.age - o.age;
       }
   }
}

主类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Tina", 20));
        students.add(new Student("Cindy", 21));
        students.add(new Student("Cathy", 21));
        students.add(new Student("Helen", 24));
        students.add(new Student("Alen", 20));
        Collections.sort(students);
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

以Student对象的年龄升序排列,输出结果:

Student{name=‘Alen’, age=20}
Student{name=‘Tina’, age=20}
Student{name=‘Cathy’, age=21}
Student{name=‘Cindy’, age=21}
Student{name=‘Helen’, age=24}

在主类中,先加入Cindy后加入Cathy,排序后变成了先Cathy后Cindy,这是因为在重写compareTo方法时定义了年龄相同时按名字升序排列。

使用Comparator比较器完成对象排序

Comparator其实就是把比较方法单独拿了出来,使用时单独传入sort方法里。改用Comparator只需改动部分代码:

Student类:

public class Student {
    String name;
    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 +
                '}';
    }
}

主类:

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<Student> students = new ArrayList<>();
        students.add(new Student("Tina", 20));
        students.add(new Student("Cindy", 21));
        students.add(new Student("Cathy", 21));
        students.add(new Student("Helen", 24));
        students.add(new Student("Alen", 20));
        Collections.sort(students, new Comparator<Student>() {
            @Override
            public int compare(Student o1, Student o2) {
                if(o1.age == o2.age) {
                    return o1.name.compareTo(o2.name);
                } else {
                    return o1.age - o2.age;
                }
            }
        });
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

Student类正常写,主类中可以看到sort方法后传入了两个参数,第一个是要排序的集合,第二个就是comparator比较器了,这里直接new Comparator接口后加花括号的方法是匿名接口实现,ta等同于如下:
新建StudentComparator类实现Comparator接口:

import java.util.Comparator;

public class StudentComparator implements Comparator<Student> {
    @Override
    public int compare(Student o1, Student o2) {
        if(o1.age == o2.age) {
            return o1.name.compareTo(o2.name);
        } else {
            return o1.age - o2.age;
        }
    }
}

Collections.sort改为:

	StudentComparator comparator = new StudentComparator();
    Collections.sort(students, comparator);

输出结果与上面一致

Collecitons.sort的底层实现

1. 使用Comparator的实现

首先开启debug调试,进入Collecitons.sort方法
在这里插入图片描述
Collections.sort调用的是集合对象内置的sort方法,单步进入list.sort
在这里插入图片描述

这里的expectedModCount是出于线程安全考虑而增设的变量,我们把重点放在Arrays.sort方法这一句上(可以看到Colletions.sort本质上还是Arrays.sort),继续单步进入
在这里插入图片描述
rangeSort是检查数组范围是否合法,不合法会抛出异常
LegacyMergeSort,追踪其位置,可以看到官方解释

Old merge sort implementation can be selected (for
compatibility with broken comparators) using a system property.
Cannot be a static boolean in the enclosing class due to
circular dependencies. To be removed in a future release.

意思就是这是一种老的归并排序,现在暂时不会用了,未来会移除,所以返回值为flase。TimSort是对归并排序做了大量优化的新版本,所以程序会跳转到TimSort.sort方法中,完成对象排序。

2. 实现Comparable接口的实现

在这里插入图片描述
唯一一点与Comparator不一致的地方是c为null,因为我们没有显式传入Comparator对象。
在这里插入图片描述
进入sort方法后,其内部调用的是ComparableTimSort,与TimSort没有区别。

  • 1
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java中,ComparableComparator都是用于进行对象比较的接口。它们的用法如下: 1. Comparable接口 Comparable接口是Java内置的接口,它包含一个方法compareTo(),用于比较对象的大小。实现该接口的类可以直接进行排序。 例如,我们定义一个Person类实现Comparable接口: ``` public class Person implements Comparable<Person> { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } public int compareTo(Person person) { // 按照年龄进行排序 return this.age - person.age; } } ``` 在这个例子中,我们通过实现Comparable接口,并重写compareTo()方法,按照年龄进行排序。 使用Comparable接口进行排序的例子: ``` List<Person> list = new ArrayList<Person>(); list.add(new Person("Tom", 20)); list.add(new Person("Jerry", 18)); list.add(new Person("Jack", 25)); Collections.sort(list); for(Person p : list) { System.out.println(p.getName() + " " + p.getAge()); } ``` 输出结果: ``` Jerry 18 Tom 20 Jack 25 ``` 2. Comparator接口 Comparator接口也是Java内置的接口,它包含一个方法compare(),用于比较两个对象的大小。实现该接口的类可以定制不同的比较规则。 例如,我们定义一个PersonComparator类实现Comparator接口: ``` public class PersonComparator implements Comparator<Person> { public int compare(Person p1, Person p2) { // 按照姓名进行排序 return p1.getName().compareTo(p2.getName()); } } ``` 在这个例子中,我们通过实现Comparator接口,并重写compare()方法,按照姓名进行排序。 使用Comparator接口进行排序的例子: ``` List<Person> list = new ArrayList<Person>(); list.add(new Person("Tom", 20)); list.add(new Person("Jerry", 18)); list.add(new Person("Jack", 25)); Collections.sort(list, new PersonComparator()); for(Person p : list) { System.out.println(p.getName() + " " + p.getAge()); } ``` 输出结果: ``` Jack 25 Jerry 18 Tom 20 ``` 总之,ComparableComparator都是用于对象比较的接口。使用Comparable接口可以方便地对实现该接口的对象进行排序,而使用Comparator接口可以定制不同的比较规则。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值