Java编程:Comparable和Comparator接口

博客内容:对docs的个人翻译、综合多个博客的总结表格、程序练习

Public Interface Comparable

  • 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类对象排序。
  • Java编程:Comparable和Comparator接口_第1张图片
  • 保证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: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有很多方法(略)

 

Comparable<T>

Comparator<T>

相同点:都是用于对象比较、排序

 

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 elist=new ArrayList();

{elist.add….}

Collections.sort(elist);

TreeSet eset=new TreeSet();

{eset.add…}

 

public class CmpTmp implements Comparator<Tmp>{

         public int compare(Tmp e1, Tmp e2){

                   // compare e1 and e2

         }

}

ArrayList elist=new ArrayList ();

{elist.add….}

CmpTmp cmp=new CmpTmp();

Collections.sort(elist, cmp);

cmp.compare(e1,e2);

TreeSet eset=new TreeSet(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 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 pset1=new TreeSet();
        pset1.add(new Person("Ada",22));
        pset1.add(new Person("Bob",11));
        System.out.println("Comparable: "+pset1);

        TreeSet pset2=new TreeSet(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 {
    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 {
    @Override
    public int compare(Person p1, Person p2) {  //比较器按年龄排序
        return p1.getAge()

输出:

Java编程:Comparable和Comparator接口_第2张图片

你可能感兴趣的:(Java)