Comparable和Comparator的知识点以及两者的区别

在这里插入图片描述

一. 问题背景

在了解TreeMap实现原理的过程中,了解到它必须实现Comparable或Comparator。为什么要实现这个接口,两者有什么区别?看到TreeMap源码有注释写着如下:

     * Returns the comparator used to order the keys in this map, or
     * {@code null} if this map uses the {@linkplain Comparable
     * natural ordering} of its keys.

上面的注释说明了comparator用来对TreeMap中的key排序。如果没有实现comparator接口,TreeMap将使用key实现的Comparable接口对key进行自然排序。

总结:这两个接口都是用来对map中的key进行排序的

本文对Comparable和Comparator的知识点进行总结,此笔记仅供自己参考,如有错误请指正。为了能正确的分析其知识点与区别,笔者尽量从TreeMap源码去分析。下面会涉及比较多TreeMap的源码,英文也比较多。

参考自:Comparable与Comparator的区别

二. Comparable

查看源码,如下:

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

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

由此得出 Comparable是在java.lang包下的。且只有一个方法compareTo(T o)

package java.lang;import java.util.*;下面可以看到有一段implement notes注释,我们从源码注释并结合他人的博客去学习Comparable。如下:

   This interface imposes a total ordering on the objects of each class that
 * implements it.  This ordering is referred to as the class's <i>natural
 * ordering</i>, and the class's <tt>compareTo</tt> method is referred to as
 * its <i>natural comparison method</i>.<p>

大概意思:接口(指Comparable接口)强制类(实现了Comparable接口的类)中的对象 进行全排序。这个排序会被默认当作是类的自然排序(natural ordering),并且类的compareTo()方法被当作实现此排序的方法(排序的规则由此方法实现)。

Comparable的使用方法:Collections.sort()Arrays.sort(),如下:

   Lists (and arrays) of objects that implement this interface can be sorted
 * automatically by {@link Collections#sort(List) Collections.sort} (and
 * {@link Arrays#sort(Object[]) Arrays.sort}).  Objects that implement this
 * interface can be used as keys in a {@linkplain SortedMap sorted map} or as
 * elements in a {@linkplain SortedSet sorted set}, without the need to
 * specify a {@linkplain Comparator comparator}.<p>

大概意思:由 实现了Comparable接口的对象 组成的list或数组,可以被Collections.sort()Arrays.sort()排序。现了Comparable接口的对象能作为SortedMapSortedSet的key(用这种key,SortedMapSortedSet就无需确定一个Comparator了)。

总结:从最后一句话可以得出Comparator的优先级比Comparable高。 即如果既有实现Comparable接口,又有实现Comparator接口,那么起到排序作用的是Comparator。

再继续看public int compareTo(T o)方法的注释。如下:

       Compares this object with the specified object for order.  Returns a
     * negative integer, zero, or a positive integer as this object is less
     * than, equal to, or greater than the specified object.

大概意思:当前对象与确定的对象作比较,会返回一个负整数、0、正整数三者中的一个。并且负整数代表小于,0代表等于,正整数代表大于。

也就是说假如有this.compareTo(o2),当:

返回值代表的意思
负整数this < o2
0this = 0
正整数this > o2
       <p>In the foregoing description, the notation
     * <tt>sgn(</tt><i>expression</i><tt>)</tt> designates the mathematical
     * <i>signum</i> function, which is defined to return one of <tt>-1</tt>,
     * <tt>0</tt>, or <tt>1</tt> according to whether the value of
     * <i>expression</i> is negative, zero or positive.

大概意思:根据上面的描述,方法的返回值:负整数,0,正整数,分别用-1,0,1代替。

       @throws NullPointerException if the specified object is null
     * @throws ClassCastException if the specified object's type prevents it
     * from being compared to this object.

大概意思: 如果入参为null,抛出NullPointerException如果入参的类型与当前对象的类型不一致,抛出ClassCastException类型转换异常。

从上面得知Comparable是在java.lang包下的,联想到java的基础类(比如Integer)也是此包下,去查看有没有实现Comparable接口。如下:

public final class Integer extends Number implements Comparable<Integer> {

Integer实现了Comparable接口。因此我们可以看看其排序的规则默认是怎么样的?什么时候会返回-1、0、1?是默认升序还是降序?

找到compareTo()方法,如下:

public int compareTo(Integer anotherInteger) {
        return compare(this.value, anotherInteger.value);
    }

点进去看compare()方法,如下:

 public static int compare(int x, int y) {
        return (x < y) ? -1 : ((x == y) ? 0 : 1);
    }

可以看到x代表this.value,y是anotherInteger(即被比较的对象)。当前对象<被比较对象,返回-1;否则继续判断两者是否相等,若相等返回0,否则返回1。

总结:也就是-1代表的实际意义是升序的,那么1就是降序的了。也可以理解成-1代表不进行排序,直接结束。1代表进行排序

2.1 例子

2.1.1 按User对象的age升序排序

首先User要实现Comparable接口,重写compareTo()方法

User.java

package com.atguigu;

public class User implements Comparable<User>{
    private String name;
    private Integer age;

    public User() {
    }

    public User(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    @Override
    public int compareTo(User o) {
        return (this.age-o.age);
    }
}

Main.java

 @Test
    public void test5() {
        List<User> users = new ArrayList<>();
        users.add(new User("tianqi", 7));
        users.add(new User("zhangsan", 3));
        users.add(new User("zhaoliu", 6));
        users.add(new User("wuwang", 5));
        System.out.println("排序前:" + users);
        Collections.sort(users);
        System.out.println("排序后" + users);
    }

测试结果:

在这里插入图片描述

2.1.1 按User对象的age降序排序

只需将User中的compareTo()方法的排序规则修改即可,即修改return后的语句,用被比较对象.age-this.age

User.java

@Override
    public int compareTo(User o) {
        return (o.age-this.age);
    }

测试结果:

在这里插入图片描述

总结:从实现Comparable进行排序的过程中,可以发现, 若使用Comparable进行排序,那么在定义类之处就必须考虑好排序的需求。当类定义好了,不能再随意修改,那么此时我要修改Comparable的排序规则是不推荐的。 比如Integer实现了Comparable接口,默认采用升序排序。我要改为降序,此时是显然不推荐去修改Comparable的排序规则(即jdk的源码)。那要怎么修改呢?使用Comparator接口

三. Comparator

package java.util;

Comparator位于java.util下

源码的implements notes有这样一段,如下:

* A comparison function, which imposes a <i>total ordering</i> on some
 * collection of objects.  Comparators can be passed to a sort method (such
 * as {@link Collections#sort(List,Comparator) Collections.sort} or {@link
 * Arrays#sort(Object[],Comparator) Arrays.sort}) to allow precise control
 * over the sort order.  Comparators can also be used to control the order of
 * certain data structures (such as {@link SortedSet sorted sets} or {@link
 * SortedMap sorted maps}), or to provide an ordering for collections of
 * objects that don't have a {@link Comparable natural ordering}.<p>

大概意思:该接口的功能是用于比较,它强制Objects集合进行全排序。通过Collections.sort(List,Comparator)Arrays.sort(Object[],Comparator)方法,能进行精确的排序。 Comparators也被用于数据结构中(比如SortedSetSortedMap、一些没有实现Comparable接口的Objects集合)

Unlike {@code Comparable}, a comparator may optionally permit
 * comparison of null arguments, while maintaining the requirements for
 * an equivalence relation.

大概意思:与Comparable不同,comparator允许空参比较。

 * Note: It is generally a good idea for comparators to also implement
 * <tt>java.io.Serializable</tt>, as they may be used as ordering methods in
 * serializable data structures (like {@link TreeSet}, {@link TreeMap}).  In
 * order for the data structure to serialize successfully, the comparator (if
 * provided) must implement <tt>Serializable</tt>.<p>

大概意思:comparators实现序列化也是非常好的,因为它们经常被用于在数据结构(比如TreeSetTreeMap)排序方法中。为了数据结构能成功地序列化,comparator必须实现Serializable接口。

@FunctionalInterface
public interface Comparator<T> {

Comparator是一个函数式接口。可以使用lamda表达式。jdk1.8新增的内容

3.1 例子

3.1.1 对User对象的升序排序

  @Test
    public void test6() {
        List<User> users = new ArrayList<>();
        users.add(new User("tianqi", 7));
        users.add(new User("zhangsan", 3));
        users.add(new User("zhaoliu", 6));
        users.add(new User("wuwang", 5));
        System.out.println("排序前" + users);
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                return (o1.getAge()-o2.getAge());
//                return (o2.getAge()-o1.getAge());
            }
        });
        System.out.println("排序后" + users);
    }

测试结果:.

在这里插入图片描述

3.1.2 对User对象的降序排序

只需修改return后的语句即可,如下:

@Override
            public int compare(User o1, User o2) {
//                return (o1.getAge()-o2.getAge());
                return (o2.getAge()-o1.getAge());
            }

3.1.3 对User对象的年龄为null排序

与Comparable不同,Comparator允许空参排序。需求:对User对象的age进行降序排序,age为null的排在最后

 @Test
    public void test6() {
        List<User> users = new ArrayList<>();
        users.add(new User("tianqi", null));
        users.add(new User("zhangsan", 3));
        users.add(new User("zhaoliu", null));
        users.add(new User("wuwang", 5));
        System.out.println("排序前" + users);
        Collections.sort(users, new Comparator<User>() {
            @Override
            public int compare(User o1, User o2) {
                if(o1.getAge() != null && o2.getAge() != null) {
                    return (o2.getAge()-o1.getAge());
                }else {
                    return o1.getAge() == null ? 1 : -1;
                }
            }
        });
        System.out.println("排序后" + users);
    }

测试结果:

在这里插入图片描述

需求:对User对象的age进行降序排序,age为null的排在最前。

只需将compare()中的第二个return改为return o1.getAge() == null ? -1 : 1;,如下:

  public int compare(User o1, User o2) {
                if(o1.getAge() != null && o2.getAge() != null) {
                    return (o2.getAge()-o1.getAge());
                }else {
                    return o1.getAge() == null ? -1 : 1;
                }
            }

测试结果:

在这里插入图片描述

总结:使用Comparator接口排序,需要在集合外定义Comparator接口的方法

四. Comparable和Comparator的区别

  1. Comparator位于包java.util下,而Comparable位于包 java.lang下
  2. 使用Comparable接口实现排序,排序的规则(即重写compareTo()方法)是定义在Bean类里面
  3. 使用Comparator接口实现排序,排序的规则(即重写compare()方法)是定义在外部(即不是Bean类里面)
  4. 用 Comparator 是策略模式(strategy design pattern),就是不改变对象自身,而用一个策略对象(strategy object)来改变它的行为。
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值