概述
Java常用的两个排序接口是Comparable和Comparator。
Comparable表示可被排序的,实现该接口的类的对象自动拥有排序功能,可以看做是自己和自己比较。
Comparator则表示一个比较器,实现了该接口的的类的对象是一个针对目标类的对象定义的比较器,一般情况,这个比较器将作为一个参数进行传递。
Comparable
Comparable的中文意思就是可被排序的,代表本身支持排序功能。只要我们的类实现了这个接口,那么这个类的对象就会自动拥有了可被排序的能力。而且这个排序被称为类的自然顺序。这个类的对象的列表可以被Collections.sort和Arrays.sort来执行排序。同时这个类的实例具备作为sorted map的key和sorted set的元素的资格。
源码解析:
package java.lang;
import java.util.*;
public interface Comparable<T> {
public int compareTo(T o);
}
Comparable是一个泛型接口。该接口会对它的实现类的对象施加一个全序,这个序被称为是实现类的自然顺序,实现类中具体实现的compareTo方法被称为该类的自然比较方法。
关于该接口有以下说明:
(1)某个类的自然顺序被称为它的equals方法是一致的,是指对类的除null的所有实例(instance)x和y,x.compareTo(y)==0和x.equals(y)有相同的boolean值,false或true。事实上x.compareTo(null)会抛NullPointerException空指针异常,当x不为null时,x.equals(null)会返回false,x为null时也会抛出NullPointerException异常。强烈建议类的compareTo方法实现与equals方法实现保持一致。
例子:
package test;
public class Person implements Comparable<Person>{
private String name;
private Integer age;
public Person(String name,Integer age)
{
this.name=name;
this.age=age;
}
//按照属性age定义了自然属性
@Override
public int compareTo(Person o) {
return this.age-o.age;
}
}
package test;
import javafx.collections.transformation.SortedList;
import java.util.SortedSet;
import java.util.TreeSet;
public class CompareTest {
public static void main(String[] args) {
Person p1=new Person("zhangsan",10);
Person p2=new Person("lisi",10);
Person p3=new Person("wangmazi",10);
SortedSet<Person> s=new TreeSet();
boolean flag1=s.add(p1);
boolean flag2=s.add(p2);
boolean flag3=s.add(p3);
System.out.println("flag1"+flag1);
System.out.println("flag2"+flag2);
System.out.println("flag3"+flag3);
System.out.println(s.size());
}
}
输出结果:
Person类实现了Comparable接口,按照属性age定义了自然顺序,但是没有重写equals方法,直接继承了父类Object的equals方法。在下面的测试中,创建了Person的三个对象p1,p2,p3。按照equals方法判断,是三个不同的对象。但是按照序来判断,三个对象的序是相同的,当加入一个有序集合时,p2和p3会加入失败。所以在实现自然顺序时应该与equals相同。
(2)实现了Comparable的所有Java核心类的自然顺序和equals方法是一致,一个特例是java.math.BigDecimal。
package test;
import javafx.collections.transformation.SortedList;
import java.math.BigDecimal;
import java.util.SortedSet;
import java.util.TreeSet;
public class CompareTest {
public static void main(String[] args) {
BigDecimal d1=new BigDecimal("2.0");
BigDecimal d2=new BigDecimal("2.00");
System.out.println("d1的精度:"+d1.scale());
System.out.println("d2的精度:"+d2.scale());
System.out.println("d1.equals(d2)"+d1.equals(d2));
System.out.println("d1.comparaeTo(d2)"+d1.compareTo(d2));
}
}
输出结果:
对于BigDecimal类,equals会比较值得精度,所以不相等返回false。而compareTo比较大小,值相等返回true。
public int compareTo(T o)
该方法用于比较当前对象(this object)和给定对象(specified object)的序,它的返回值是负整数,零,正整数分别对应当前对象小于,等于和大于给定对象。compareTo在子类的具体实现有以下几点必须保证的:
(1)对所有的对象x和y满足sgn(x.compareTo(y))等于-sgn(y.compareTo(x)),要么x.compareTo(y)抛出异常当且仅当x.compareTo(y)抛异常。这里sgn(x)是一个数学符号函数,当x>0时,sgn(x)=1;当x=0时,sgn(x)=0;当x<0时,sgn(x)=-1;
(2)如果x.compareTo(y)大于0,并且y.compareTo(z)大于0,那么x.compareTo(z)一定大于0;
(3)如果x.compareTo(y)等于0,那么对所有的z满足sgn(x.compareTo(z))和sgn(x.compareTo(z))相等;
Comparator
Comparator中文译为比较器,它可以作为一个参数传递到Collections.sort和Arrays.sort方法来指定某个类对象的排序方式。同时它也能为sorted set和sorted map指定排序方式。
同Comparable类似,指定比较器的时候一般也要保证比较的结果与equals结果一致,不一致的话,对应的sorted set和sorted map的行为同样会变得怪异。
推荐实现的比较器类同时实现java.io.Serializable接口,以拥有序列化能力,因为它可能会被用作序列化的数据结构(TreeSet、TreeMap)的排序方法。
源码解析:
@FunctionalInterface
public interface Comparator<T> {
// 唯一的抽象方法,用于定义比较方式(即排序方式)
// o1>o2,返回1;o1=o2,返回0;o1<o2,返回-1
int compare(T o1, T o2);
boolean equals(Object obj);
// 1.8新增的默认方法:用于反序排列
default Comparator<T> reversed() {
return Collections.reverseOrder(this);
}
// 1.8新增的默认方法:用于构建一个次级比较器,当前比较器比较结果为0,则使用次级比较器比较
default Comparator<T> thenComparing(Comparator<? super T> other) {
Objects.requireNonNull(other);
return (Comparator<T> & Serializable) (c1, c2) -> {
int res = compare(c1, c2);
return (res != 0) ? res : other.compare(c1, c2);
};
}
// 1.8新增默认方法:指定次级比较器的
// keyExtractor表示键提取器,定义提取方式
// keyComparator表示键比较器,定义比较方式
default <U> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
return thenComparing(comparing(keyExtractor, keyComparator));
}
// 1.8新增默认方法:用于执行键的比较,采用的是由键对象内置的比较方式
default <U extends Comparable<? super U>> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor)
{
return thenComparing(comparing(keyExtractor));
}
// 1.8新增默认方法:用于比较执行int类型的键的比较
default Comparator<T> thenComparingInt(ToIntFunction<? super T> keyExtractor) {
return thenComparing(comparingInt(keyExtractor));
}
// 1.8新增默认方法:用于比较执行long类型的键的比较
default Comparator<T> thenComparingLong(ToLongFunction<? super T> keyExtractor) {
return thenComparing(comparingLong(keyExtractor));
}
// 1.8新增默认方法:用于比较执行double类型的键的比较
default Comparator<T> thenComparingDouble(ToDoubleFunction<? super T> keyExtractor) {
return thenComparing(comparingDouble(keyExtractor));
}
// 1.8新增静态方法:用于得到一个相反的排序的比较器,这里针对的是内置的排序方式(即继承Comparable)
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
// 1.8新增静态方法:用于得到一个实现了Comparable接口的类的比较方式的比较器
// 简言之就是将Comparable定义的比较方式使用Comparator实现
@SuppressWarnings("unchecked")
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) Comparators.NaturalOrderComparator.INSTANCE;
}
// 1.8新增静态方法:得到一个null亲和的比较器,null小于非null,两个null相等,如果全不是null,
// 则使用指定的比较器比较,若未指定比较器,则非null全部相等返回0
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(true, comparator);
}
// 1.8新增静态方法:得到一个null亲和的比较器,null大于非null,两个null相等,如果全不是null,
// 则使用指定的比较器比较,若未指定比较器,则非null全部相等返回0
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
return new Comparators.NullComparator<>(false, comparator);
}
// 1.8新增静态方法:使用指定的键比较器用于执行键的比较
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & Serializable)
(c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
keyExtractor.apply(c2));
}
// 1.8新增静态方法:执行键比较,采用内置比较方式,key的类必须实现Comparable
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
}
// 1.8新增静态方法:用于int类型键的比较
public static <T> Comparator<T> comparingInt(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
}
// 1.8新增静态方法:用于long类型键的比较
public static <T> Comparator<T> comparingLong(ToLongFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
}
// 1.8新增静态方法:用于double类型键的比较
public static<T> Comparator<T> comparingDouble(ToDoubleFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
}
}
Comparator<T>是一个比较器接口,被比较的类不需要实现它。
package test;
public class Person {
private String name;
private Integer age;
public Person(String name,Integer age)
{
this.name=name;
this.age=age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
//按照属性age定义了自然属性
@Override
public String toString() {
return "Person{"+"name="+name+"\"+ age"+age+'}';
}
}
package test;
import java.util.Comparator;
public class DesComparator implements Comparator<Person>
{
@Override
public int compare(Person o1, Person o2) {
return o1.getAge()-o2.getAge();
}
}
package test;
import javafx.collections.transformation.SortedList;
import java.math.BigDecimal;
import java.util.*;
public class CompareTest {
public static void main(String[] args) {
Person p1=new Person("zhangsan",10);
Person p2=new Person("lisi",29);
Person p3=new Person("wangmazi",25);
Person p4=new Person("wangd",34);
List<Person> personList=new ArrayList<>();
personList.add(p1);
personList.add(p2);
personList.add(p3);
personList.add(p4);
System.out.println("排序前"+personList);
Collections.sort(personList,new DesComparator());
System.out.println("排序后:"+personList);
}
}
输出结果:
想对Person的对象按照年龄进行降序排序,定义一个降序比较器(实现了Comparator接口),在测试的时候,这个比较器作为参数传递给Collection.sort方法。
说明:Collections是一个工具类,sort是其中的静态方法,是用来对List类型进行排序的,它有两种参数形式。上面调用的就是第二种形式。
public static <T extends Comparable<? super T>> void sort(List<T> list) {
list.sort(null);
}
public static <T> void sort(List<T> list, Comparator<? super T> c) {
list.sort(c);
}
Comparable和Comparator区别
Comparable | Comaparator | |
接口所在的包 | java.lang.Comaprable | java.util.Comarator |
排序的区别 | 排序方法必须待在待排序的类中,故称为自然排序 | 不必在一个类中 |
排序方法 | int compareTo(Objcet o1) | int compare(Object o1,Object o2) |
排序的调用 | Collection.sort(list) | Collections.sort(list,Comparator) |
- 对于Comparable接口来说,被比较对象所属的类需要直接实现Comparable接口,实现该接口的类被赋予一个自然顺序,而且该自然顺序只有一个,而Comparator是一个比较器接口,被比较对象所属的类不需要直接实现该接口,可以单独写一个比较器类实现该接口,作为比较对象的一个比较器,对于一个类来说,可以实现多个比较器。
- Comparator可以选择对null进行比较,而Comparable不可以。主要是因为Comparator的比较对象都是是compare方法的参数,而Comparable的比较方法compareTo方法需要对象来调用,而对象为null时(null.compareTo(obj)),会出现异常。
参考文章:
https://blog.csdn.net/L_15156024189/article/details/84196414