Java Comparator与Comparable

Java从1.2开始添加了一些很实用的工具,可以对数组或者列表进行排序。今天这篇博文主要介绍对列表进行排序相关的两个接口Comparator与Comparable,直接翻译可以译为比较器和可以比较的,Compartor是定义在java.util包下的,而Comparable是定义下java.lang包下的。

对列表进行排序实际上是Collections的静态方法,上面提到了对列表排序中常使用的两个接口,事实上与这两个接口所对应的是Collections类中两个重载的静态方法,方法的定义格式如下:

//方法一
public static <T extends Comparable<? super T>> void sort(List<T> list) {
	//...
}
//方法二
public static <T> void sort(List<T> list, Comparator<? super T> c) {
	//...
}

Comparable

Comparable定义非常简单,里面只有一个compareTo方法,该接口定义如下:

public interface Comparable<T> {
     * @param   o the object to be compared.
     * @return  a negative integer, zero, or a positive integer as this object
     *          is less than, equal to, or greater than the specified object.
     *
     * @throws NullPointerException if the specified object is null
     * @throws ClassCastException if the specified object's type prevents it
     *         from being compared to this object.
     */
    public int compareTo(T o);
}

在《Java编程思想》中作者这么描述该类:“通过实现Comparable接口,它采用了赋予一个类的‘自然比较方法’”。为什么说是自然比较方法呢?如果我们实现了该接口,通过自已定义的规则来重写compareTo方法,能分别根据它小于、等于或者大于自变量而返回负数、零或者正数,从而实现对象的比较。举个栗子,代码如下:

public class User implements Comparable<User> {

	private String username;
	private int age;
	
	public User(String username,int age){
		this.username=username;
		this.age=age;
	}

	//...

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

	private static List<User> list;

	public static void main(String[] args) {
		list = getData();
		//fff, aaa, bbb, ccc, eee, ddd, 张三, 李四
		System.out.println("List:" + list.toString());

		Collections.sort(list);
		//fff, eee, 张三, ddd, 李四, aaa, bbb, ccc
		System.out.println("List:" + list.toString());
	}

	private static List<User> getData() {
		List<User> list = new ArrayList<>();
		list.add(new User("fff", 16));
		list.add(new User("aaa", 20));
		list.add(new User("bbb", 21));
		list.add(new User("ccc", 22));
		list.add(new User("eee", 18));
		list.add(new User("ddd", 19));
		list.add(new User("张三", 18));
		list.add(new User("李四", 19));
		return list;
	}

}

通过上面我们自定义的compareTo方法,User会按照年龄升序排序,当我们使用username属性来重写逻辑:

@Override
public int compareTo(User o) {
	return this.getUsername().compareTo(o.getUsername());
}
//aaa, bbb, ccc, ddd, eee, fff, 张三, 李四

因为字符串我们不能用加减来进行比较,所以我们使用了字符串的compareTo方法,查看一下源代码我们发现String字符串已经实现了Comparable接口,实现的核心方法如下:

public int compareTo(String anotherString) {
	int len1 = value.length;
	int len2 = anotherString.value.length;
	int lim = Math.min(len1, len2);
	char v1[] = value;
	char v2[] = anotherString.value;

	int k = 0;
	while (k < lim) {
		char c1 = v1[k];
		char c2 = v2[k];
		if (c1 != c2) {
			return c1 - c2;
		}
		k++;
	}
	return len1 - len2;
}

对字符串进行长度比较,使用较短的一个字符串作为比较次数,然后字符串中的字符转化为整形进行比较,所以中文字符无法进行比较。事实上开发中比较常用的日期类型java.util.Date也已经实现了Comparable,可以直接使用compareTo方法对日期进行比较。

在实现compareTo方法时遵循以下规则:

  • 如果要按照升序排序,则当前对象小于入参,返回-1(负数),相等返回0,01大于02返回1(正数);
  • 如果要按照降序排序,则当前对象大于入参,返回1(正数),相等返回0,01小于02返回-1(负数)。

Comparator

Comparator接口定义比Comparable多了一个equals方法,定义如下:

public interface Comparator<T> {
    /**
     * @param o1 the first object to be compared.
     * @param o2 the second object to be compared.
     * @return a negative integer, zero, or a positive integer as the
     *         first argument is less than, equal to, or greater than the
     */
    int compare(T o1, T o2);

    /**
     * @param   obj   the reference object with which to compare.
     * @return  true only if the specified object is also
     *          a comparator and it imposes the same ordering as this
     *          comparator.
     */
    boolean equals(Object obj);
}

还是先举个栗子,从栗子中学习使用,也是对一个对象中年龄进行排序:

public class MainTest {
	
	private static List<Person> list;
	public static void main(String[] args) {
		list=getData();
		//fff, aaa, bbb, ccc, eee, ddd, 张三, 李四
		System.out.println("List:"+list.toString());
		
		Collections.sort(list,new PersonCompare());
		//fff, eee, 张三, ddd, 李四, aaa, bbb, ccc
		System.out.println("List:"+list.toString());
	}
	
	private static List<Person> getData(){
		List<Person> list=new ArrayList<>();
		list.add(new Person("fff",16));
		list.add(new Person("aaa",20));
		list.add(new Person("bbb",21));
		list.add(new Person("ccc",22));
		list.add(new Person("eee",18));
		list.add(new Person("ddd",19));
		list.add(new Person("张三",18));
		list.add(new Person("李四",19));
		return list;
	}
	
}
public class PersonCompare implements Comparator<Person>{
	@Override
	public int compare(Person o1, Person o2) {
		return o1.getAge()-o2.getAge();
	}
}

从输出结果我们可以看到也是按照升序排序的,在实现compare方法时遵循以下规则:

  • 如果要按照升序排序,则o1小于o2,返回-1(负数),相等返回0,01大于02返回1(正数);
  • 如果要按照降序排序,则o1大于o2,返回1(正数),相等返回0,01小于02返回-1(负数)。

两种排序比较

Comparable是定义在对象内部的,也就是说如果我们使用的对象继承了Comparable,排序比较的规则就确定了,但是如果使用的是Comparator,我们可以实现多种规则,只需要使用不同的比较器就可以实现按照不同字段排序比较的要求。可以说前者属于 “静态绑定”,而后者可以 “动态绑定”。Comparable 相当于 “内部比较器”,而 Comparator 相当于“外部比较器”,“外部比较器”灵活多变,“内部比较器”简单直接。

评论

您确定要删除吗?删除之后不可恢复