effective java 之考虑实现Comparable接口
1、什么是Comparable接口?
Comparable接口一般用于表示某个实例具有内在的排序关系。简单来说就是用于对象排序。
2、为什么需要实现Comparable接口?
我们可以对数值和字符串进行排序,是因为系统内部已经为我们定义了数值和字符串的排序关系。
而我们定义的对象,本身是不包含排序关系的,因此,我们无法直接对对象进行排序。
如果我们需要对对象进行排序的话,就必须定义对象的内在排序关系,即实现Comparable接口。
3、如何实现Comparable接口
我们举个具体的例子,我们定义一个person对象,要求person按照年龄来进行排序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int compareTo(Person o) {
if (this.age > o.age)
return 1;
else if (this.age < o.age)
return -1;
else
return 0;
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public static void printList(List<Person> list) {
for (Person p : list) {
System.out.println(p);
}
System.out.println();
}
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("John", 38));
list.add(new Person("Marry", 21));
list.add(new Person("Tom", 20));
System.out.println("Before sort:");
printList(list);
Collections.sort(list);
System.out.println("After sort:");
printList(list);
}
}
Before sort:
Person [name=John, age=38]
Person [name=Marry, age=21]
Person [name=Tom, age=20]
After sort:
Person [name=Tom, age=20]
Person [name=Marry, age=21]
Person [name=John, age=38]
//升序排序
@Override
public int compareTo(Person o) {
if (this.age > o.age)
return 1;
else if (this.age < o.age)
return -1;
else
return 0;
}
//降序排序
@Override
public int compareTo(Person o) {
if (this.age < o.age)
return 1;
else if (this.age > o.age)
return -1;
else
return 0;
}
4、多重排序
假设age相同时,对name进行排序。
public class Person implements Comparable<Person> {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
//多重排序
@Override
public int compareTo(Person o) {
if (age > o.age)
return 1;
if (age < o.age)
return -1;
//若年龄相等,则直接通过名字来进行排序。
return name.compareTo(o.name);
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
public static void printList(List<Person> list) {
for (Person p : list) {
System.out.println(p);
}
System.out.println();
}
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("John", 38));
list.add(new Person("Tom", 20));
list.add(new Person("Marry", 20));
System.out.println("Before sort:");
printList(list);
Collections.sort(list);
System.out.println("After sort:");
printList(list);
}
}
Before sort:
Person [name=John, age=38]
Person [name=Tom, age=20]
Person [name=Marry, age=20]
After sort:
Person [name=Marry, age=20]
Person [name=Tom, age=20]
Person [name=John, age=38]
实际上,也就是当年龄相等时,可以直接调用name的compare方法来进行排序。
因为字符串在系统中已经设置好了内部排序。
这里默认为升序,那如果需要设置降序呢?
return -name.compareTo(o.name);
修改为其相反数即可。
5、如果为Person类添加一个id属性,并要求先按id升序,若id相同,则按年龄降序,若id和age都相同,则按name升序。
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class Person implements Comparable<Person> {
private int id;
private String name;
private int age;
public Person(int id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// 先排序谁,就把谁放到最前面
@Override
public int compareTo(Person o) {
if (id > o.id)
return 1;
if (id < o.id)
return -1;
// id相等的情况下,对age进行排序
if (age > o.age)
return -1;
if (age < o.age)
return 1;
// 若id和age相等,则直接通过名字来进行排序。
return name.compareTo(o.name);
}
@Override
public String toString() {
return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";
}
public static void printList(List<Person> list) {
for (Person p : list) {
System.out.println(p);
}
System.out.println();
}
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person(3, "John", 38));
list.add(new Person(1, "Tom", 20));
list.add(new Person(1, "Marry", 20));
System.out.println("Before sort:");
printList(list);
Collections.sort(list);
System.out.println("After sort:");
printList(list);
}
}
Before sort:
Person [id=3, name=John, age=38]
Person [id=1, name=Tom, age=20]
Person [id=1, name=Marry, age=20]
After sort:
Person [id=1, name=Marry, age=20]
Person [id=1, name=Tom, age=20]
Person [id=3, name=John, age=38]
6、实现Comparable接口所需满足的需求
满足对称性:
必须确保所有的x和y都满足sgn(x.compareTo(y)) == -sgn(y.compareTo(x));
满足传递性:
若(x.compareTo(y) >0 && y.compareTo(z)>0),则x.compareTo(z)>0。
若x.compareTo(y)==0,则sgn(x.compareTo(z)) == sgn(y.compareTo(z));
7、与Comparator的区别
Comparator位于包java.util下,而Comparable位于包java.lang下,Comparable接口将比较代码嵌入自身类中,而后者在一个独立的类中实现比较。
如果类的设计师没有考虑到Compare的问题而没有实现Comparable接口,可以通过 Comparator来实现比较算法进行排序,并且为了使用不同的排序标准做准备,比如:升序、降序。
我们看一个Comparator的例子:
import java.util.TreeSet;
import java.util.Comparator;
class NumComparator implements Comparator<NameTag> {
public int compare (NameTag left,NameTag right) {
return(left.getNumber() - right.getNumber());
}
}
public class CollectionNine {
public static void main(String arg[]) {
new CollectionNine();
}
CollectionNine() {
NumComparator comparator = new NumComparator();
TreeSet<NameTag> set = new TreeSet<NameTag>(comparator);
set.add(new NameTag("Agamemnon",300));
set.add(new NameTag("Cato",400));
set.add(new NameTag("Plato",100));
set.add(new NameTag("Zeno",200));
set.add(new NameTag("Archimedes",500));
for(NameTag tag : set)
System.out.println(tag);
}
}
每天努力一点,每天都在进步。