容器-Comparable和Comparator

概述

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区别

 ComparableComaparator
接口所在的包java.lang.Comaprablejava.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

https://www.jianshu.com/p/f9870fd05958

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值