/**
*@ author StormMaybin
*@ date 2016-10-01
*/
生命不息,奋斗不止!
首先,Comparator和Comparable都是接口,具有共同的功能,对于一个实体类进行排序的功能。
强行对某个对象 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 的成员。
此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的 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。。。
package java.util;
public interface Comparator
{
int compare(T o1, T o2);
boolean equals(Object obj);
}
说明:
若一个类要实现Comparator接口:它一定要实现compareTo(T o1, T o2) 函数,但可以不实现 equals(Object obj) 函数。
为什么可以不实现 equals(Object obj) 函数呢? 因为任何类,默认都是已经实现了equals(Object obj)的。 Java中的一切类都是继承于java.lang.Object,在Object.java中实现了equals(Object obj)函数;所以,其它所有的类也相当于都实现了该函数。
int compare(T o1, T o2) 是“比较o1和o2的大小”。返回“负数”,意味着“o1比o2小”;返回“零”,意味着“o1等于o2”;返回“正数”,意味着“o1大于o2”。
package java.lang;
import java.util.*;
public interface Comparable
{
public int compareTo(T o);
}
说明
Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。
而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。
package com.stormma.test;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
* 测试类
* @author StormMaybin
*/
public class ComparatorAndComparableDemo
{
public static void main(String[] args)
{
//定义一个ArrayList容器
ArrayList persons = new ArrayList();
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);
}
}
/**
* Person类实现Comparable,按姓名升序排列
* @author StormMaybin
*/
class Person implements Comparable
{
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;
}
}
/**
* 定义AscAgeComparator比较器实现Comparator接口
* 对Person类的age属性进行升序排列
* @author StormMaybin
*/
class AscAgeComparator implements Comparator
{
@Override
public int compare(Person o1, Person o2)
{
// TODO Auto-generated method stub
return o1.getAge() - o2.getAge();
}
}
/**
* 定义DscAgeComparator比较器实现Comparator接口
* 对Person类的age属性进行降序排列
* @author StormMaybin
*/
class DscAgeComparator implements Comparator
{
@Override
public int compare(Person o1, Person o2)
{
// TODO Auto-generated method stub
return -(o1.getAge() - o2.getAge());
}
}
/**
* 定义AscAgeAscNameComparator比较器实现Comparator接口
* 对Person类的age name 属性升序排列
* @author StormMaybin
*/
class AscAgeAscNameComparator implements Comparator
{
@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充当一个比较器的作用,不同实体类排序可以依据同一个比较器。