一、简介
1、Comparable 接口简介
- Comparable<T>接口:位于java.lang包下,需要重写public int compareTo(T o);
- Comparable 是排序接口。若一个类实现了Comparable接口,就意味着“该类支持排序”。
- “实现Comparable接口的类的对象”可以用作“有序映射(如TreeMap)”中的键或“有序集合(TreeSet)”中的元素,而不需要指定比较器。
- 它强行将实现它的每一个类的对象进行整体排序-----称为该类的自然排序,实现此接口的对象列表和数组可以用Collections.sort(),和Arrays.sort()进行自动排序;
- 接口中通过compareTo(T o)来比较x和y的大小。若返回负数,意味着x比y小;返回零,意味着x等于y;返回正数,意味着x大于y。
2、Comparator 接口简介
- Comparator<T>接口:位于java.util包下,需要重写int compare(T o1, T o2);
Comparator 是比较器接口。我们若需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);
它是针对一些本身没有比较能力的对象(数组)为它们实现比较的功能,所以它叫做比较器,是一个外部的东西,通过它定义比较的方式,再传到Collection.sort()和Arrays.sort()中对目标排序,而且通过自身的方法compare()定义比较的内容和结果的升降序;
int compare(T o1, T o2) 和上面的x.compareTo(y)类似,定义排序规则后返回正数,零和负数分别代表大于,等于和小于。
二、 代码实现(具体案例)
1.创建一个汽车的实体类Car 继承Comparable接口(排序接口),并重写compareTo方法
//让实体类Car继承Comparable接口
public class Car implements Comparable<Car> {
private String brand;
private int price;
public Car() {
super();
}
public Car(String brand, int price) {
super();
this.brand = brand;
this.price = price;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((brand == null) ? 0 : brand.hashCode());
result = prime * result + price;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Car other = (Car) obj;
if (brand == null) {
if (other.brand != null)
return false;
} else if (!brand.equals(other.brand))
return false;
if (price != other.price)
return false;
return true;
}
@Override
public String toString() {
return "Car [brand=" + brand + ", price=" + price + "]";
}
//重写compareTo()方法
@Override
public int compareTo(Car o) {
return this.price-o.price;
}
}
2.创建测试类Test01
public class Test01{
public static void main(String[] args) {
//创建一个TreeMap集合,用来存放Car实体类对象
TreeMap<Car, String> treeMap=new TreeMap<Car, String>();
//向TreeMap集合中添加四个具体汽车类对象
treeMap.put(new Car("宏碁汽车", 43), "动力十足!");
treeMap.put(new Car("奔驰s", 38), "奔驰牌2");
treeMap.put(new Car("奔驰s", 35), "奔驰牌1");
treeMap.put(new Car("五菱", 50), "五菱宏光,动力十足!");
//通过map.entry方法拿到一个个封装好的对象集合
Set<Map.Entry<Car, String>> set=treeMap.entrySet();
//通过增强for循环,遍历每个对象
for (Entry<Car, String> entry : set) {
System.out.println(entry);
}
}
运行结果为:
通过上述运行结果,可以看出我们通过继承Comparable接口,重写compareTo()方法,在方法中我们return的是this.price-o.price;即排序规则是按照价格price从低到高排序,输出结果符合。现在我们修改return为o.price-this.price;观察输出结果,是否按照price由高到低输出。
3.使用comparator接口(比较器接口),重写compare(T o1, T o2)方法。
TreeMap<Car, String> treeMap = new TreeMap<Car, String>(new Comparator<Car>() {
//通过匿名内部类实现Comparator接口,重写compare方法,定义比较器的排序规则
@Override
public int compare(Car o1, Car o2) {
if (o1.getBrand().length() == o2.getBrand().length()) {
return o1.getPrice() - o2.getPrice();
}
return o1.getBrand().length() - o2.getBrand().length();
}
});
4.创建测试类Test02(此时需要将Car实体类中继承的Comparable接口先删除掉)
public class Test02{
public static void main(String[] args) {
//创建一个TreeMap集合,用来存放Car实体类对象
TreeMap<Car, String> treeMap = new TreeMap<Car, String>(new Comparator<Car>() {
//通过匿名内部类实现Comparator接口,重写compare方法,定义比较器的排序规则
@Override
public int compare(Car o1, Car o2) {
if (o1.getBrand().length() == o2.getBrand().length()) {
return o1.getPrice() - o2.getPrice();
}
return o1.getBrand().length() - o2.getBrand().length();
}
});
//向TreeMap集合中添加四个具体汽车类对象
treeMap.put(new Car("宏碁汽车", 43), "动力十足!");
treeMap.put(new Car("奔驰s", 38), "奔驰牌2");
treeMap.put(new Car("奔驰s", 35), "奔驰牌1");
treeMap.put(new Car("五菱", 50), "五菱宏光,动力十足!");
//通过map.entry方法拿到一个个封装好的对象集合
Set<Map.Entry<Car, String>> set=treeMap.entrySet();
//通过增强for循环,遍历每个对象
for (Entry<Car, String> entry : set) {
System.out.println(entry);
}
}
运行结果为:
由匿名内部类中实现的Comparator接口中重写的Compare(Car o1,Car o2)方法中定义的排序规则看出当品牌名长度相同时,比较价格price,从低到高显示;当品牌名长度不等时,比较品牌名长度,从长度小的到长度大的显示。从运行结果来看符合排序规则。
此时,可能有的小伙伴会有一个疑惑当同时实现了Comparable接口和Comparator接口时,遵从谁定义的排序规则呢?那就随小编一起操作,将Car实体类中继承的Comparable接口重新继承,然后再次运行Test02,观察结果。
此时 我们会发现运行结果,使用的是Comparator接口实现类中定义的排序规则,而不是继承了Comparable接口中定义的按照价格price从高到低排序规则。
Tips:当我们同时实现了Comparable接口和Comparator接口时,谁离的近就用谁定义的排序规则(就近原则)
三、区别
- Comparable接口位于java.lang包下;Comparator位于java.util包下
- Comparable接口只提供了一个compareTo()方法;
- Comparator接口不仅提供了compara()方法,还提供了其他默认方法,如reversed()、thenComparing(),使我们可以按照更多的方式进行排序
- 如果要用Comparable接口,则必须实现这个接口,并重写comparaTo()方法;
- Comparator接口可以在类外部使用,通过将该接口的一个匿名类对象当做参数传递给Collections.sort()方法或者Arrays.sort()方法实现排序。
- Comparator体现了一种策略模式,即可以不用要把比较方法嵌入到类中,而是可以单独在类外部使用,这样我们就可有不用改变类本身的代码而实现对类对象进行排序。
四、总结
- 学习Comparable接口和Comparator接口本质的不同在于使用的时机;
- 如果你想给这个类通过一种顺序排序,那就实现Comparable接口;
- 如果你不想实现Comparable接口,想用这个像算法一样的方式去实现,那就直接在测试类写一个Comparator接口的实现类的对象就好了,在其中实现你想要的排序方式即可!
小编的话:编程不仅仅要学,还要灵活的去用。
要明白条条大路通成功!要开发自己的思维,激发自我的编程之魂~