Java 7之基础 - Comparable与Comparator



1、Comparable接口:需要一个具体的类实现


一个类实现了 Camparable 接口表明这个类的对象之间是可以相互比较的。如果用数学语言描述的话就是这个类的对象组成的集合中存在一个全序。这样,这个类对象组成的集合就可以使用 Sort 方法排序了。

这个接口定义在java.lang包下,其源代码如下:

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

这个接口支持泛型,在实现compareTo()方法时可限定具体的比较类型。下面来举一个例子,如下:

public class PersonBean implements Comparable<PersonBean> {
	public PersonBean(int age, String name) {
		this.age = age;     // 引用类型
	        this.name = name;   // 基本类型
	}
	int age = 0;
	String name = "";
	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;
	}
        // 覆写equals和hashCode方法
	public boolean equals(Object o) {
		if (!(o instanceof PersonBean)) {
			return false;
		}
		PersonBean p = (PersonBean) o;
		return (age == p.age) && (name.equals(p.name));
	}

	public int hashCode() {
		int result = 17;
		result = 31 * result + age;
		result = 31 * result + name.hashCode();
		return result;
	}
	public String toString() {
		return (age + "{" + name + "}");
	}

	public int compareTo(PersonBean person) {
		int cop = age - person.getAge();
		if (cop != 0)
			return cop;
		else
			return name.compareTo(person.name);
	}
}

如果两个人的年龄相同,则通过字符串类中的compareTo()方法进行比较。来看String类的定义如下:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence 
可以看到实现了Comparable接口并且还实现了接口中定义的方法,源代码如下:

 public int compareTo(String anotherString) {
        int len1 = value.length;
        int len2 = anotherString.value.length;
        int lim = Math.min(len1, len2);   //  返回len1和len2中长度较小的一个
        char v1[] = value;
        char v2[] = anotherString.value;

        int k = 0;
        while (k < lim) {       // 根据字符序列中所含字符的Unicode编码值来比较
            char c1 = v1[k];
            char c2 = v2[k];
            if (c1 != c2) {
                return c1 - c2;
            }
            k++;
        }
        return len1 - len2;     // 如果两个字符串的k个字符相等,则长度较小的在前
    }

字符串最后都是转换为字符数组进行比较的。

运行如上的代码:
public class testComparabl {
	public void compare() {
		PersonBean[] p = { new PersonBean(20, "Tom"),
				new PersonBean(20, "Jeff"), new PersonBean(30, "Mary"),
				new PersonBean(20, "Ada"), new PersonBean(40, "Walton"),
				new PersonBean(61, "Peter"), new PersonBean(20, "Bush") };
		
		System.out.println("before sort:\n" + Arrays.toString(p));
		Arrays.sort(p);
		System.out.println("after sort:\n" + Arrays.toString(p));
	}

	public static void main(String[] args) {
		testComparabl tc = new testComparabl();
		tc.compare();
	}

}
结果如下:
before sort:
[20{Tom}, 20{Jeff}, 30{Mary}, 20{Ada}, 40{Walton}, 61{Peter}, 20{Bush}]
after sort:
[20{Tom}, 20{Jeff}, 20{Bush}, 20{Ada}, 30{Mary}, 40{Walton}, 61{Peter}]






2、Comparator接口:被用于各个需要比较功能的类使用。


作用有两个:

  1. 如果类的设计师没有考虑到 Compare 的问题而没有实现 Comparable 接口,可以通过 Comparator 来实现比较算法进行排序;
  2. 为了使用不同的排序标准做准备,比如:升序、降序或其他什么序。

这个接口定义在com.util包下,源代码如下:

public interface Comparator<T> {
    int compare(T o1, T o2);
    boolean equals(Object obj);
}

在这个接口中定义了两个方法。

class PersonBean{    // 没有实现Comarable接口
	public PersonBean(int age, String name) {
		this.age = age;
		this.name = name;
	}

	int age = 0;
	String name = "";

	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;
	}
	public String toString() {
		return (age + "{" + name + "}");
	}
}

可以不用实现equals()方法,因为类默认继承Object对象,Object对象中提供了对equals()方法的实现。

public class AlphDesc implements Comparator<PersonBean> {            // 自定义排序规则
	public int compare(PersonBean personA, PersonBean personB) {
		int cop = personA.age - personB.age;
		if (cop != 0)
			return cop;
		else
			return personB.getName().compareTo(personA.getName());
	}
}

运行如上程序:

PersonBean[] p = { new PersonBean(20, "Tom"),
				new PersonBean(20, "Jeff"), new PersonBean(30, "Mary"),
				new PersonBean(20, "Ada"), new PersonBean(40, "Walton"),
				new PersonBean(61, "Peter"), new PersonBean(20, "Bush") };
		
		System.out.println("before sort:\n" + Arrays.toString(p));
		AlphDesc desc = new AlphDesc();
		Arrays.sort(p, desc);
		System.out.println("after sort:\n" + Arrays.toString(p));

运行结果如下:
before sort:
[20{Tom}, 20{Jeff}, 30{Mary}, 20{Ada}, 40{Walton}, 61{Peter}, 20{Bush}]
after sort:
[20{Tom}, 20{Jeff}, 20{Bush}, 20{Ada}, 30{Mary}, 40{Walton}, 61{Peter}]


3、equals()方法与Comarable、Comparator接口的比较


        equals()方法定义在Object类中并且提供了简单的实现。而Java中所有的类都继承了equals()方法,类似于Comarable接口,土生土长的,在编写源代码时就定义成这样了。所以你无法改变Object类中定义的equals()方法、或者是不可变类String的compareTo()方法。

        为了能够按照自己的规则进行比较,Object类中的equals()方法没有加final关键字,允许进行覆盖重写,类似于Comparator接口,允许自定义比较规则。但是与Comparable接口和Comparator接口比较起来,显得功能不足,专业水准不够。主要体现在如下几个方面:

(1)接口支持范型,可以传入具体的比较类型,而equals()方法以Object类型做为参数,太泛化,比较时容易出问题。如:

public class test1 {
	public static void main(String args[]) {
		AA aa = new AA();
		BB bb = new BB();
		aa.equals(bb); // 调用aa中的equals()方法
		bb.equals(aa); // 调用bb中的equals()方法
	}
}

class AA {
}

class BB {
}
不同类型比较容易出问题,如传入错误的类型在编译时无法纠正,不同的实例调用的方法决定了不同的比较规则等等。

(2)equals()是方法,更加注重面向对象的思想(继承覆盖),而接口是类型,面向的是业务和功能。所以说接口范围更大,在Comparable接口中甚至定义了equals()方法来满足其比较的功能。


下面来比较一下两个接口:

Comparable 是通用的接口,用户可以实现它来完成自己特定的比较,而 Comparator 可以看成一种算法的实现,在需要容器集合实现比较功能的时候,来指定这个比较器,这可以看成一种设计模式,将算法和数据分离。


如果想要了解Arrays.sort()方法的源代码,请点击:

http://www.cnblogs.com/gw811/archive/2012/10/04/2711746.html






你可能感兴趣的:(Java 7之基础 - Comparable与Comparator)