1.Comparable
Comparable
是排序接口。此接口强行对实现它的每个类的对象进行整体排序。这种排序被称为类的自然排序,类的compareTo()
方法被称为它的自然比较方法。
如果一个类实现了Comparable
接口,就意味着该类”支持排序”。实现此接口的对象列表(或数组)可以通过Collections.sort()
(或Arrays.sort()
)进行自动排序。实现此接口的对象可以用作有序映射中的键(比如TreeMap
中的键)或者有序集合中的元素(比如TreeSet
中的元素),而无需指定比较器。
Comparable
接口仅仅只包括一个compareTo(Object obj)
函数,如下所示:
public interface Comparable<T>{
public int compareTo(T o);
}
假设我们通过x.compareTo(y)
来比较x
和y
的大小。若该方法返回正整数,则意味着x > y
;若该方法返回负整数,则意味着x < y
;若该方法返回0,则意味着x == y
。
2.Comparator
Comparator
是比较器接口。我们可以将Comparator
传递给sort
方法(比如Collections.sort()
或者Arrays.sort()
),从而对一个Collection
实现精准排序。还可以通过Comparator
来控制某些数据结构(比如TreeSet
或者TreeMap
)的顺序。
也就是说,我们如果要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable
接口),那么我们就可以建立一个“该类的比较器”来进行排序。这个“比较器”只需要实现Comparator
接口即可。
Comparator
接口包括int compare(T o1,T o2)
和equals()
两个函数:
public interface Comparator<T>{
int compare(T o1, T o2);
boolean equals(Object obj);
}
要知道的是:
- (1)若一个类实现了
Comparator
接口,它一定要实现compare(T o1,T o2)
方法,但可以不实现equals(Object obj)方法。为什么呢?因为任何类都默认继承于Object
类,在Object
类中实现了equals()
方法,所以相当于其他所有的类都已经实现了equals()
方法。 - (2)
int compare(T o1,T o2)
方法是比较o1
和o2
的大小,该方法返回正整数,意味着o1
比o2
大;返回负整数,意味着o1
比o2
小;返回0,意味着o1
与o2
相等。
3.Comparable与Comparator的比较
Comparable
是排序接口;若一个类实现了Comparable
接口,就意味着“该类支持排序”
而Comparator
是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。
我们不难发现:Comparable
相当于“内部比较器”,而Comparator
相当于“外部比较器”。
下面我们举一个例子。
首先我们自定义一个People
类,如下:
public class People implements Comparable<People>{
private String name;
private int age;
public String getName() {
return name;
}
public int getAge() {
return age;
}
public People(String name, int age) {
super();
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
/**
* 两个People对象的name属性和age属性都相等,就认为两个对象相等
*/
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
People other = (People) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return name + "---" + age;
}
/**
* 默认的排序,按照name属性排序
*/
@Override
public int compareTo(People o) {
return this.name.compareTo(o.name);
}
}
People
类有name
和age
两个属性,重写了equals()
和hashCode()
方法,并且重写了toString()
方法。最重要的是,People
类实现了Comparable
接口,并重写了compareTo(People o)
方法,该方法让People
类默认支持排序,并且排序的依据是name
属性的字母序。
接着,我们可以在主方法中测试:
public class Test {
public static void main(String[] args) {
System.out.println("----------ArrayList------------");
ArrayList<People> array = new ArrayList<>();
array.add(new People("ddd",20));
array.add(new People("aaa",30));
array.add(new People("ccc",10));
array.add(new People("bbb",40));
System.out.println("原始序列:"+array);
// 对array进行排序,按照People类默认的排序方法,即按照name属性进行升序排列
Collections.sort(array);
System.out.println("Name升序:"+array);
}
}
程序输出:
原始序列:[ddd---20, aaa---30, ccc---10, bbb---40]
Name升序:[aaa---30, bbb---40, ccc---10, ddd---20]
可以看到,我们采用ArrayList
保存了4个People
类对象,并调用Collections.sotr()
方法对该List
进行排序,排序之前是原始序列;排序之后,按照People
类中的定义,默认按照name
属性的升序排列。
除此之外,我们可以自定义Comparator
类对象,使之按照age
属性进行排列。
比如,我们可以按照age
属性的升序排列:
public class AscAgeComparator implements Comparator<People> {
@Override
public int compare(People o1, People o2) {
return o1.getAge() - o2.getAge();
}
}
自定义AscAgeComparator
类,实现Comparator
接口,并重写compare(People o1, People o2)
方法,该方法按照age
属性进行升序排列。
或者我们也可以按照age
属性进行降序排列,如下定义DescAgeComparator
:
public class DescAgeComparator implements Comparator<People> {
@Override
public int compare(People o1, People o2) {
return o2.getAge() - o1.getAge();
}
}
这时候我们在调用Collections.sort()
方法时,也可以给该方法传递一个Comparator
对象,使之按照该对象自定义的排序方式进行排序:
public class Test {
public static void main(String[] args) {
System.out.println("----------ArrayList------------");
ArrayList<People> array = new ArrayList<>();
array.add(new People("ddd",20));
array.add(new People("aaa",30));
array.add(new People("ccc",10));
array.add(new People("bbb",40));
System.out.println("原始序列:"+array);
// 通过AscAgeComparator比较器进行排序,根据age升序排列
Collections.sort(array, new AscAgeComparator());
System.out.println("Age升序:"+array);
// 通过DescAgeComparator比较器进行排序,根据age降序排列
Collections.sort(array, new DescAgeComparator());
System.out.println("Age降序:"+array);
}
}
程序输出:
原始序列:[ddd---20, aaa---30, ccc---10, bbb---40]
Age升序:[ccc---10, ddd---20, aaa---30, bbb---40]
Age降序:[bbb---40, aaa---30, ddd---20, ccc---10]
4.Comparator的应用之实现Map按照value值进行排序
这里补充一个Comparator
的一个很实用的应用例子。
Map
本身并不支持按照value
值进行排序,TreeMap
实现的也是按照key
值进行排序,那么如何实现Map
按照value
值进行排序呢?这时候我们可以将该Map
转换成List<Map.Entry<T,T>>
,也就是把Map
中每一个Entry
键值对保存在List
中,再自定义Comparator
,对该List
进行排序即可。
比如,我们再自定义People
类如下:
public class People {
private int age;
private String name;
public int getAge() {
return age;
}
public String getName() {
return name;
}
public People( String name,int age) {
super();
this.age = age;
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ((name == null) ? 0 : name.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
People other = (People) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
@Override
public String toString() {
return name + "---" + age;
}
}
这次,People
类并没有实现Comparable
接口,也就是说People
类不支持排序。
我们将People
类对象作为value
值保存在Map
中,做如下测试:
public class Test {
public static void main(String[] args) {
HashMap<String, People> hm = new HashMap<>();
hm.put("1", new People("ddd",20));
hm.put("2", new People("ccc",40));
hm.put("3", new People("aaa",30));
hm.put("4", new People("bbb",10));
System.out.println("排序之前:");
hm.forEach((key,value) -> System.out.println(key + " " + value));
// 将Map中的每一个键值对转化为Entry对象,保存在List中
List<Entry<String, People>> list = new ArrayList<>(hm.entrySet());
// 自定义Comparator,按照People对象的年龄升序进行排序
Comparator<Map.Entry<String, People>> valueComparator = new Comparator<Map.Entry<String,People>>() {
@Override
public int compare(Entry<String, People> o1, Entry<String, People> o2) {
return o1.getValue().getAge() - o2.getValue().getAge();
}
};
// 排序,比较器为我们自定义的valueComparator
Collections.sort(list, valueComparator);
System.out.println("排序之后:");
//Java8中支持的遍历输出:
list.forEach((item -> System.out.println(item.getKey() + " " + item.getValue())));
}
}
最后我们按照People
对象的age
属性进行升序排列,程序输出:
排序之前:
1 ddd---20
2 ccc---40
3 aaa---30
4 bbb---10
排序之后:
4 bbb---10
1 ddd---20
3 aaa---30
2 ccc---40