先看一个示例:
public class Person {
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
/**
* 重写 toString 方法
* @return
*/
@Override
public String toString() {
return "["+this.name + "," + this.age + "]";
}
}
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
//声明定义一个元素类型为 Person 类型的数组
Person[] persons = {new Person("张三", 22),new Person("李四", 11),new Person("王五", 33)};
//先输出排序前的数组
System.out.println(Arrays.toString(persons));
//对数组进行排序
Arrays.sort(persons);
//输出排序后的数组
System.out.println(Arrays.toString(persons));
}
}
打印结果:
报 java.lang.ClassCastException 异常,即 Person 类型的实例对象不能向上转型转换成 Comparable 接口类型进行比较,原因是 Person 类并没有实现 Comparable 接口。
在排序的过程中为什么要把 Person 类型的实例对象向上转型转 Comparable 接口类型?
因为如果直接对 Person 类型的实例对象进行大小比较是没有比较的依据,我们需要对 Person 类型的实例对象大小比较制定规则,需要让 Person 类实现 Comparable 接口并重写接口中的 compareTo() 方法,即对接口中重写的 compare() 方法就是用来指定比较 Person 类型的实例对象大小的规则的。
Comparable 接口
一个类实现了 Comparable 接口就表示该类在定义的时候就得重写 Comparable 接口中的 compareTo() 方法,指定比较该类类型实例对象比较大小的规则。
Comparable 接口源码:
public interface Comparable<T> {
public int compareTo(T o);
}
在 Comparable 接口中只有一个 compareTo(T o) 方法,此方法的返回值是 int 类型的整数,因此我们在重写该方法的时候是通过返回的 int 类型的整数来判断两个对象的大小的。
(1)> 0 如果方法的调用对象 - 参数对象,返回值为正整数,即表示调用对象大于参数对象;
(2)< 0 如果方法的调用对象 - 参数对象,返回值为负整数,即表示调用对象小于参数对象;
(3)== 0 如果方法的调用对象 - 参数对象,返回值为 0,即表示调用对象等于参数对象;
所以要对上面的 Person 类进行修改,实现 Comparable 接口,并重写 compareTo 方法。
示例:
public class Person implements Comparable<Person>{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
/**
* 重写 toString 方法
* @return
*/
@Override
public String toString() {
return "["+this.name + "," + this.age + "]";
}
/**
* 重写比较器的方法,用年龄排序
* @param o the object to be compared.
* @return
*/
@Override
public int compareTo(Person o) {
if(this.age > o.age) {
return 1;
}
if(this.age < o.age) {
return -1;
}
return 0;
}
}
再进行比较:
import java.util.Arrays;
public class Test {
public static void main(String[] args) {
//声明定义一个元素类型为 Person 类型的数组
Person[] persons = {new Person("张三", 22),new Person("李四", 11),new Person("王五", 33)};
//先输出排序前的数组
System.out.println(Arrays.toString(persons));
//对数组进行排序
Arrays.sort(persons);
//输出排序后的数组
System.out.println(Arrays.toString(persons));
}
}
打印结果:
此时就可以完成 Person 类型实例对象的大小关系比较,具体依据的是 Person 类型实例对象的 age 的大小。
Comparator 接口
Comparator 接口被称之为可挽救的比较器,如果一个类在定义的时候并没有实现 Comparable 接口,也就表示无法比较该类类型的实例对象的大小,但是现在确实需要比较该类类型的实例对象的大小,由于类已经声明定义好了,无法修改了,只能考虑在不修改源类的基础上,也能够制定较该类类型的实例对象的大小规则,此时就可以用可挽救的比较器 Comparator 接口。
Comparator 接口部分源码:
@FunctionalInterface
public interface Comparator<T> {
int compare(T o1, T o2);
//省略部分...
}
在 Comparator 接口中同样有一个用来比较两个对象大小关系的 compare(T o1, T o2) 方法,同样使用 int 类型的返回值来表示参与比较的两个对象的大小关系。
(1)> 0 如果返回值为正整数,即表示对象 o1 大于 o2;
(2)< 0 如果返回值为正整数,即表示对象 o1 小于 o2;
(3)== 0 如果返回值为 0,即表示对象 o1 等于 o2;
没有实现 Comparable 接口的情况:
public class Person{
private String name;
private int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
/**
* 重写 toString 方法
* @return
*/
@Override
public String toString() {
return "["+this.name + "," + this.age + "]";
}
}
这样依旧可以进行比较:
import java.util.Arrays;
import java.util.Comparator;
public class Test {
public static void main(String[] args) {
//声明定义一个元素类型为 Person 类型的数组
Person[] persons = {new Person("张三", 22),new Person("李四", 11),new Person("王五", 33)};
//先输出排序前的数组
System.out.println(Arrays.toString(persons));
//对数组进行排序
Arrays.sort(persons, new Comparator<Person>() {
// 重写 compare 方法
@Override
public int compare(Person o1, Person o2) {
if(o1.getAge() < o2.getAge()) {
return -1;
}
if(o1.getAge() > o2.getAge()) {
return 1;
}
return 0;
}
});
//输出排序后的数组
System.out.println(Arrays.toString(persons));
}
}
打印结果: