java中Comparator和Comparable接口区别分析

/**
*@ author StormMaybin
*@ date 2016-10-01
*/

生命不息,奋斗不止!


首先,Comparator和Comparable都是接口,具有共同的功能,对于一个实体类进行排序的功能。

Comparator简介

      强行对某个对象 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)}. 此接口是 Java Collections Framework 的成员。

Comparable简介

此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 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)}。

以上文字来自jdk1.6 API。。。

Comparator 定义
package java.util;
public interface Comparator<T>
{
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

说明:

  1. 若一个类要实现Comparator接口:它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
    为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。

  2. int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。

Comparable 定义
package java.lang;
import java.util.*;
public interface Comparable<T> 
{
    public int compareTo(T o);
}

说明

  • 假设我们通过 x.compareTo(y) 来“比较x和y的大小”。若返回“负数”,意味着“x比y小”;返回“零”,意味着“x等于y”;返回“正数”,意味着“x大于y”。
Comparator 和 Comparable 比较

Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

代码演示
package com.stormma.test;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
 * <em>测试类</em>
 * @author StormMaybin
 */
public class ComparatorAndComparableDemo 
{

    public static void main(String[] args) 
    {
        //定义一个ArrayList容器
        ArrayList<Person> persons = new ArrayList<Person>();
        String [] names = {"StormMa", "Jack", "Rose", "Mary"};
        int [] ages = {20, 21, 21, 18};
        for (int i = 0; i < 4; i++)
            persons.add(new Person(names[i], ages[i]));

        //排序之前
        System.out.println("排序之前: "+persons);


        //按照Person类中实现Comparable接口的规则排序。这里的规则是按照姓名排序
        Collections.sort(persons);
        System.out.println("按照姓名排序: "+persons);


        //通过AscAgeComparator比较器进行排序,即按照年龄升序排列
        Collections.sort(persons, new AscAgeComparator());
        System.out.println("按照年龄升序排序"+persons);


        //通过DscAgeComparator比较器进行排序,即按照年龄降序排列
        Collections.sort(persons, new DscAgeComparator());
        System.out.println("按照年龄降序排序"+persons);


        //通过AscAgeAscNameComparator比较器进行排序
        //即按照首先按照年龄升序,年龄相同的情况下,
        //按照姓名升序排列
        Collections.sort(persons, new AscAgeAscNameComparator());
        System.out.println("按照年龄和姓名升序排列: "+persons);
    }
}


/**
 * <em>Person类实现Comparable,按姓名升序排列</em>
 * @author StormMaybin
 */
class Person implements Comparable<Person>
{
    private String name;
    private int age;
    //Constructor
    public Person (String name, int age)
    {
        this.name = name;
        this.age = age;
    }
     /**
     * 比较两个Person是否相等:若它们的name和age都相等,则认为它们相等
     */
    boolean equals(Person o) 
    {
        if (this.age == o.age && this.name == o.name)
            return true;
        return false;
    }
    /**
     * 实现Comparable接口,这里只针对姓名进行排序
     */
    @Override
    public int compareTo(Person o) 
    {
        // TODO Auto-generated method stub
        return this.name.compareTo(o.name);
    }
    @Override
    public String toString() 
    {
        // TODO Auto-generated method stub
        return "( name: " + name +","+ "age: "+age+")";
    }
    public int getAge() 
    {
        return age;
    }
    public void setAge(int age)
    {
        this.age = age;
    }
    public String getName() 
    {
        return name;
    }
    public void setName(String name) 
    {
        this.name = name;
    }
}

/**
 * <em>定义AscAgeComparator比较器实现Comparator接口
 * 对Person类的age属性进行升序排列</em>
 * @author StormMaybin
 */
class AscAgeComparator implements Comparator<Person>
{

    @Override
    public int compare(Person o1, Person o2) 
    {
        // TODO Auto-generated method stub
        return o1.getAge() - o2.getAge();
    }
}


/**
 * <em>定义DscAgeComparator比较器实现Comparator接口
 * 对Person类的age属性进行降序排列</em>
 * @author StormMaybin
 */
class DscAgeComparator implements Comparator<Person>
{

    @Override
    public int compare(Person o1, Person o2) 
    {
        // TODO Auto-generated method stub
        return -(o1.getAge() - o2.getAge());
    }
}
/**
 * <em>定义AscAgeAscNameComparator比较器实现Comparator接口
 * 对Person类的age name 属性升序排列</em>
 * @author StormMaybin
 */
class AscAgeAscNameComparator implements Comparator<Person>
{

    @Override
    public int compare(Person o1, Person o2) 
    {
        // TODO Auto-generated method stub
        if (o1.getAge() == o2.getAge())
            return o1.getName().compareTo(o2.getName());
        else
            return o1.getAge() - o2.getAge();
    }
}

测试结果:
这里写图片描述

上述例子足以看出,Comparable是让一个实体类具有排序的能力,而Comparator充当一个比较器的作用,不同实体类排序可以依据同一个比较器。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值