博客内容:对docs的个人翻译、综合多个博客的总结表格、程序练习
Public Interface Comparable<T>
- T: the type of objects that this object may be compared to。要被比较的对象的类型
- 为实现Comparable接口的类的对象强加impose全排序。这里的排序指类的自然顺序natural ordering,类的compareTo方法称为自然比较方法natural comparison method
- 当且仅当一个类的所有元素“e”的"e1.compareTo(e1)==0"的逻辑结果与“e1.equals(e2)”的逻辑结果相同时,称这个类的自然顺序与equals一致
- 强烈建议(但不是必须)自然顺序与equals一致,这是因为如果对象的自然顺序和equals不一致,当未指定比较器的sorted maps将其用作key,或sorted sets将其用作元素时,会出现异常,即这样的maps或sets会违反按照equals来定义的maps和sets的常规general contract。例如:为未指定比较器的sorted set加入两个元素a和b,a和b满足条件(!a.equals(b)&&a.compareTo(b))(即自然顺序与equals不一致),则加入b元素时会返回false(set的size也不会增加),因为此时在set看来a和b是等价的元素(因为虽然&&前面的"!a.equals(b)==1"表示a和b不相等,但是&&后面的"a.compareTo(b)==1"表示a和b相等——个人理解)。如果不能满足自然顺序和equals一致,需要明确声明"Note: this class has a natural ordering that is inconsistent with equals."
- 实际上几乎所有实现了Comparable接口的类都有与equals一致的自然顺序,除了java.math.BigDecimal,这个类的自然排序使其对象有相同的值,不同的精度
- 以实现了Comparable接口的类的对象为元素的列表或数组可以调用Collections.sort或Arrays.sort实现自动排序
- 实现了Comparable接口的类的对象可用作sorted maps的key,或sorted sets的元素,不用专门指定比较器comparator
- null不是任何类的实例,调用e.compareTo(null)应该抛出异常NullPointerException,尽管e.equals(null)返回值是false
- int compareTo(T o):按照指定方式为T类对象排序。
- 保证sgn(x.compareTo(y))== -sgn(y.compareTo(x)),并且x.compareTo(y)抛出异常时,y.compareTo(x)也要抛出异常
- 保证传递性:x.compareTo(y)>0&&y.compareTo(z)>0 =>x.compareTo(z)>0
- 保证当x.compareTo(y)时,sgn(x.compareTo(z)==sgn(y.compareTo(z))
- sgn(x) 是数学中的符号函数,x<0时sgn(x)=-1;x==0时sgn(x)==0;x>0时sgn(x)==1
Public Interface Comparator<T>
- T:the type of objects that may be compared by this comparator。要被此比较器比较的类
- Comparator接口是功能性的接口,可用作lambda表达式或方法引用的assignment target
- a comparison function,比较器,为collection元素impose total ordering。
- 比较器可传递给排序方法sort mehtod(如Collections.sort或Arrays.sort)来控制排序方式
- 比较器可用于控制某些数据结构(如sorted sets,sorted maps)的排序
- 比较器可为没有自然顺序的collection的元素提供排序方式
- 对于集合S中的元素的比较器c来说,当且仅当"c.compare(e1,e2)==0"的逻辑结果与"e1.equals(e2)"的逻辑结果相同时,称比较器c强加的顺序与equals一致。当二者不一致时,同上sorted maps或sets会出现异常,例如:为空TreeSet添加元素a,b,a和b满足条件"a.equals(b)&&c.compare(a,b)!=0",添加第二个元素b时会成功(set的size也增加),因为在sets看来a和b不相等,尽管这和Set.add方法的说明相悖。(Set.add(e):集合中不存在e则返回true,否则false)
- Note:如果要将比较器用于serializable data structures(如TreeSet,TreeMap)排序,则必须实现Serializable接口
- 与Comparable不同,比较器可以比较null,同时保持等价关系。
- Comparator有很多方法(略)
| ||
相同点:都是用于对象比较、排序 | ||
| Interface in java.lang (提供基础类) | Interface in java.util (提供工具类) |
| This interface imposes a total ordering on the objects of each class that implements it. 自然排序:T类自身控制比较方式 一个类实现Comparable接口即表示自身支持排序,Comparable相当于内部比较器:实现Comparable接口的类通过重写接口的compareTo方法,从自身内部控制对象的排序方式 | A comparison function, which imposes a total ordering on some collection of objects. 定制排序:T类外创建比较器,比较器控制T类对象比较方式 一个类实现Comparator接口后,就成了比较器类,它的对象就相当于外部比较器,T就是这个比较器要控制排序方式的类类型,在比较器类中以T类的对象为compare方法的参数重写compare方法,从T类外部控制T类对象的排序方式 |
方法 | int compareTo(T o) T类重写此方法指定排序方式 | int compare(T o1, T o2) 比较器类重写此方法指定T对象排序方式 |
| 适用于自定义类 (很多常见类都实现了Comparable接口:String,Integer,Enum,File...) | 适用于自定义类 或无法通过Comparable接口改变排序方式的常见类 或者用于要将比较器用于多类的情况(比如用多态) |
| 1、以T类的对象为元素的List可以调用Collections.sort()或Arrays.sort()对List排序 2、T类的对象可用作有序映射(如TreeMap)中的键Key,或有序集合(如TreeSet)中的元素,不需要指定比较器 | 1、以T类的对象为元素的List可以调用Collections.sort()或Arrays.sort()对List排序,并通过传入比较器指定T对象的排序方式 2、T类的对象可用作有序映射(如TreeMap)中的键Key,或有序集合(如TreeSet)中的元素,并通过传入比较器指定T对象的排序方式 |
例程 | public class Tmp implements Comparable<Tmp>{ public int compareTo(Tmp e){ // compare this.e and e } } ArrayList<Tmp> elist=new ArrayList<Tmp>(); {elist.add….} Collections.sort(elist); TreeSet<Tmp> eset=new TreeSet<Tmp>(); {eset.add…}
| public class CmpTmp implements Comparator<Tmp>{ public int compare(Tmp e1, Tmp e2){ // compare e1 and e2 } } ArrayList<Tmp> elist=new ArrayList<Tmp> (); {elist.add….} CmpTmp cmp=new CmpTmp(); Collections.sort(elist, cmp); cmp.compare(e1,e2); TreeSet<Tmp> eset=new TreeSet<Tmp>(cmp); {eset.add…} |
练习:
import java.util.ArrayList;
import java.util.Collections;
import java.util.TreeSet;
public class CompareTest {
public static void main(String args[]) {
ArrayList<Person> plist = new ArrayList<>();
plist.add(new Person("A", 30));
plist.add(new Person("B", 20));
plist.add(new Person("C", 10));
System.out.println("Original:" + plist);
Collections.sort(plist); // Person按姓名排序实现的Comparable
System.out.println("NameOrder:" + plist);
//PersonComparator是按年龄排序的比较器类
PersonComparator cmp = new PersonComparator();
System.out.println(plist.get(0).getName() + " age compare with " + plist.get(1).getName() + " age = " + cmp.compare(plist.get(0), plist.get(1)));
Collections.sort(plist, cmp); //指定按照年龄排序
System.out.println("AgeOrder:" + plist);
//两种方式建立Person的有序集合
TreeSet<Person> pset1=new TreeSet<Person>();
pset1.add(new Person("Ada",22));
pset1.add(new Person("Bob",11));
System.out.println("Comparable: "+pset1);
TreeSet<Person> pset2=new TreeSet<Person>(cmp);
pset2.add(new Person("ada",22));
pset2.add(new Person("Bob",11));
System.out.println("Comparator:"+pset2);
}
}
// 被比较的类:Person
import java.lang.Comparable;
public class Person implements Comparable<Person> {
private String name;
private int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
@Override
public int compareTo(Person other) { //Person按姓名排序重写Comparable的compareTo()
return name.compareTo(other.getName());
}
@Override
public String toString(){ //如果不重写自定义类的toString,输出的就是hashCode
return name+"("+age+")";
}
}
// 比较器类:PersonComparator
import java.util.Comparator;
public class PersonComparator implements Comparator<Person> {
@Override
public int compare(Person p1, Person p2) { //比较器按年龄排序
return p1.getAge()<p2.getAge()? -1:p1.getAge()==p2.getAge()? 0:1;
}
}
输出: