Comparable和Comparator的用法

Java 中基本类型的比较可以使用比较运算符,不同于C++,Java 中的对象比较是不能利用运算符重载(尽管 Java 中有运算符重载现象)。Comparable 和 Comparator 接口的存在就是为了对象比较,我们可以在接口中定义对象比较规则,还可以利用 Collections.sort 和 Arrays.sort 方法对对象数组和集合进行排序。

Comparable接口

该接口位于 java.lang 包下,接口中只有一个抽象方法 compareTo(),下面就是接口的源码 :

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

对于实现该接口的类,会被强加一个整体排序,排序规则在 compareTo 方法中进行定义,也称这种排序方式为自然排序

怎么使用这个接口呢?这里通过一个相亲的例子看我们如何通过 Comparable 接口给相亲对象做一个简单排序。一般相亲,女方会看男方有没有钱,年龄有多大等等之类的,当然,也不排除男方会在意女方有没有钱……

首先,我们定义一个 Person 类并实现 Comparable 接口:

public class Person implements Comparable<Person>{
	String name;
	int age;
	int money;
	public Person(String name, int age, int money) {
		this.name = name;
		this.age = age;
		this.money = money;
	}
	@Override
	public int compareTo(Person another) {
		//这里我们选择用金钱来对这个相亲对象进行排序,还是比较现实的哈
		if(this.money > another.money)
			return 1;
		else if(this.money == another.money)
			return 0;
		else
			return -1;
	}
}

Tips:Comparable 泛型接口中的 T 在使用时,须指明为要比较的对象类型,例如 Person

在 Person 类中,我们已经定义好了规则,假设只有两个相亲对象 Jack 和 Timi,可以直接通过 compareTo 这个方法就可以完成比较:

public class Test {
	public static void main(String[] args) {
		Person Jack = new Person("Jack", 25, 3000000);
		Person Timi = new Person("Timi", 20, 2000000);
		if(Timi.compareTo(Jack) > 0)
			System.out.println("My choice is\r" + Timi.name);
		else
			System.out.println("My choice is\r" + Jack.name);
	}
}/*output:
My choice is
Jack
*/

Tips:自然排序中的 a.compareTo(b) == 0 等价于 a.equals(b),但是 compareTo 中不允许比较 null ,否则会抛出空指针异常。

在比较规则下,当然是 Jack 比 Timi 有钱,选 Jack 没烦恼;假设相亲对象不止一个,大约有一个数组那么多,可以使用 Arrays.sort 进行排序:

public class Test {
	public static void main(String[] args) {
		Person[] men = new Person[]{
			new Person("Jack",25,3000000),
			new Person("Tom",20,2000000),
			new Person("Siri",31,10000000),
			new Person("Nacy",26,2500000)
		};
		Arrays.sort(men);
		for(Person man:men) {
			System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
		}
	}
}/*output:
Tom 	20	2000000
Nacy	26	2500000
Jack	25	3000000
Siri	31	10000000
*/

如果不进行排序,相亲对象在数组中就会杂乱不堪,使用 Arrays.sort(Object[] a) 方法进行排序后,对象们根据财力的从小到大进行了一个排序。假如我们想要将财力从大到小排序,只需要修改一个比较规则,即 compareTo 方法:

public int compareTo(Person another) {
	if(this.money < another.money)
		return 1;
	else if(this.money == another.money)
		return 0;
	else
		return -1;
}

因为,Arrays.sort 只能进行对象数组的排序,如果相亲对象是放在集合中,可以使用 Collections.sort(List list) 进行排序:

public class Test {
	public static void main(String[] args) {
		ArrayList<Person> men = new ArrayList<>();
		men.add(new Person("Jack",25,3000000));
		men.add(new Person("Tom",20,2000000));
		men.add(new Person("Siri",31,10000000));
		men.add(new Person("Nacy",26,2500000));
		Collections.sort(men);
		for(Person man:men) {
			System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
		}
	}
}

Tips:使用 Arrays.sort 和 Collections.sort 进行对象排序时,所要排序的对象必须实现了 Comparable 接口。

在 java.lang 包中,可以看到基本类型的包装类们,都实现了 Comparable 接口,它们都定义了自己的比较规则。

Comparator接口

该接口位于 java.util 包下,这个接口中的方法还是比较多的,如果我们想要使用这个接口,只需要实现一个抽象方法 compare 就可以了:

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

对于实现该接口的类,我们称为比较器类,在使用 Arrays.sort 和 Collections.sort 方法进行排序时,只需要将要比较的对象和比较器传入方法,在比较器的 compare 方法中定义了比较规则,我们也称此排序方式为比较器排序

下面就是使用比较器,对相亲对象进行排序。首先,我们就是要删除 Person 类中的比较规则:

public class Person{
	String name;
	int age;
	int money;
	public Person(String name, int age, int money) {
		this.name = name;
		this.age = age;
		this.money = money;
	}
}

然后,我们定义一个 Person 类的比较器,需要实现 Comparator 类:

public class PersonComparator implements Comparator<Person>{
	@Override
	public int compare(Person o1, Person o2) {
		if(o1.money > o2.money)
			return 1;
		else if(o1.money == o2.money)
			return 0;
		else
			return -1;
	}
}

最后,我们使用 Arrays.sort(T[] a, Comparator<? super T> c) 方法 和 Collections.sort(List list, Comparator<? super T> c) 方法给对象们进行一个财力排序:

public class Test {
	public static void main(String[] args) {
		/*Person[] men = new Person[]{
			new Person("Jack",25,3000000),
			new Person("Tom",20,2000000),
			new Person("Siri",31,10000000),
			new Person("Nacy",26,2500000)
		};*/
		ArrayList<Person> men = new ArrayList<>();
		men.add(new Person("Jack",25,3000000));
		men.add(new Person("Tom",20,2000000));
		men.add(new Person("Siri",31,10000000));
		men.add(new Person("Nacy",26,2500000));
//		Arrays.sort(men, new PersonComparator());
		Collections.sort(men, new PersonComparator());
		for(Person man:men) {
			System.out.println(man.name +"\t"+ man.age +"\t"+ man.money);
		}
	}
}/*output:
Tom 	20	2000000
Nacy	26	2500000
Jack	25	3000000
Siri	31	10000000
*/

如果我们仅仅对两个相亲对象就行比较,就不能用 compareTo 方法了,理智告诉我们,Person 类中已经没有了比较规则。

那么,如果我们想要对这两个相亲对象进行比较,而又不使用对象数组或者集合,我们又该怎么实现?

public class Test {
	public static void main(String[] args) {
		Person Jack = new Person("Jack", 25, 3000000);
		Person Timi = new Person("Timi", 20, 2000000);
		PersonComparator comparator = new PersonComparator();
		if(comparator.compare(Jack, Timi) > 0) {
			System.out.println("My choice is\r" + Jack.name);
		}else {
			System.out.println("My choice is\r" + Timi.name);
		}
	}
}/*output:
My choice is
Jack
*/

如果查看 java.util 包中的集合类,我们会发现:集合类中的 sort 方法大都支持传入一个自定义的比较器,来改变默认的排序方式。

总结

1. compareTo 方法或者 compare 方法的返回值,都不要求一定返回 -1, 0, 1 ;我们只需要返回一个 正整数、负整数或者 0 即可。

所以上面的 comparaTo 方法和 comparable 方法实际上可以写成差值的形式:

public int compare(Person o1, Person o2) {
		return o1.money - o2.money;
}

2. 使用 Comparable 和 Comparator 接口时抛出空指针异常,并不代表我们不能比较空对象,只要我们在对应的方法中给出处理空对象的规则,同样可以进行下去:

public int compareTo(Person another) {
	//一个不存在的相亲对象肯定比不过这些实实在在的相亲对象
	if(another == null)
		return -1;
	if(this.money > another.money)
		return 1;
	else if(this.money == another.money)
		return 0;
	else
		return -1;
}

我们可以简单测试一下,空对象处理方法:

public class Test {
	public static void main(String[] args) {
		Person Jack = new Person("Jack", 25, 3000000);
		Person Timi = new Person("Timi", 20, 2000000);
		if(Timi.compareTo(null) > 0)
			System.out.println("My choice is\r null");
		else
			System.out.println("My choice is\r" + Jack.name);
	}
}/*output:
My choice is
Jack
*/

3. Comparable 和 Comparator 的区别:实现 Comparable 的类,说明这个类是可比较的;实现 Comparator 的类变成了一个工具类,需要比较的类可以使用这个工具类进行比较。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章