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 相当于“外部比较器”,“外部比较器”灵活多变,“内部比较器”简单直接。