Comparator和Comparable在排序中的应用

来源:互联网 发布:mac安装office2016 编辑:程序博客网 时间:2024/06/06 02:55

当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。

一、Comparator

强行对某个对象collection进行整体排序的比较函数,可以将Comparator传递给Collections.sort或Arrays.sort。

接口方法:

  /**
   * 
@return o1小于、等于或大于o2,分别返回负整数、零或正整数。
   
*/
  
int compare(Object o1, Object o2);

案例:

import java.util.Arrays;
import java.util.Comparator;

public class SampleComparator implements Comparator {

  
public int compare(Object o1, Object o2) {
    
return toInt(o1) - toInt(o2);
  }

  
private int toInt(Object o) {
    String str 
= (String) o;
    str 
= str.replaceAll("""1");
    str 
= str.replaceAll("""2");
    str 
= str.replaceAll("""3");
    
// 
    return Integer.parseInt(str);
  }

  
/**
   * 测试方法
   
*/
  
public static void main(String[] args) {
    String[] array 
= new String[] { "一二""""" };
    Arrays.sort(array, 
new SampleComparator());
    
for (int i = 0; i < array.length; i++) {
      System.out.println(array[i]);
    }
  }

}

 二、Comparable

强行对实现它的每个类的对象进行整体排序,实现此接口的对象列表(和数组)可以通过Collections.sort或Arrays.sort进行自动排序。

接口方法:

  /**
   * 
@return 该对象小于、等于或大于指定对象o,分别返回负整数、零或正整数。 
   
*/
  
int compareTo(Object o);

假设对象User,需要按年龄排序:

public class User {

  
private String id;
  
private int age;

  
public User(String id, int age) {
    
this.id = id;
    
this.age = age;
  }

  
public int getAge() {
    
return age;
  }

  
public void setAge(int age) {
    
this.age = age;
  }

  
public String getId() {
    
return id;
  }

  
public void setId(String id) {
    
this.id = id;
  }

}

改造后的对象:

import java.util.Arrays;

public class User implements Comparable {

  
private String id;
  
private int age;

  
public User(String id, int age) {
    
this.id = id;
    
this.age = age;
  }

  
public int getAge() {
    
return age;
  }

  
public void setAge(int age) {
    
this.age = age;
  }

  
public String getId() {
    
return id;
  }

  
public void setId(String id) {
    
this.id = id;
  }

  
public int compareTo(Object o) {
    
return this.age - ((User) o).getAge();
  }

  
/**
   * 测试方法
   
*/
  
public static void main(String[] args) {
    User[] users 
= new User[] { new User("a"30), new User("b"20) };
    Arrays.sort(users);
    
for (int i = 0; i < users.length; i++) {
      User user 
= users[i];
      System.out.println(user.getId() 
+ " " + user.getAge());
    }
  }

}


项目中用到的排序。

public void ascSortByPONumber() {
Collections.sort(results, new Comparator<QuoteAndFindingsSearchResultsVO>() {
public int compare(QuoteAndFindingsSearchResultsVO arg0, QuoteAndFindingsSearchResultsVO arg1) {
return arg1.getPoNumber().compareTo(arg0.getPoNumber());
}
});
}

public void descSortByPONumber() {
Collections.sort(results,new Comparator<QuoteAndFindingsSearchResultsVO>() {
public int compare(QuoteAndFindingsSearchResultsVO arg0, QuoteAndFindingsSearchResultsVO arg1) {
return arg0.getPoNumber().compareTo(arg1.getPoNumber());
}
});
}



三、Comparator和Comparable的区别

先看一下使用Comparator对User集合实现排序的方式:

import java.util.Arrays;
import java.util.Comparator;

public class UserComparator implements Comparator {

  
public int compare(Object o1, Object o2) {
    
return ((User) o1).getAge() - ((User) o2).getAge();
  }

  
/**
   * 测试方法
   
*/
  
public static void main(String[] args) {
    User[] users 
= new User[] { new User("a"30), new User("b"20) };
    Arrays.sort(users, 
new UserComparator());
    
for (int i = 0; i < users.length; i++) {
      User user 
= users[i];
      System.out.println(user.getId() 
+ " " + user.getAge());
    }
  }

}

一个类实现了Camparable接口则表明这个类的对象之间是可以相互比较的,这个类对象组成的集合就可以直接使用sort方法排序。
Comparator可以看成一种算法的实现,将算法和数据分离,Comparator也可以在下面两种环境下使用:
1、类的设计师没有考虑到比较问题而没有实现Comparable,可以通过Comparator来实现排序而不必改变对象本身
2、可以使用多种排序标准,比如升序、降序等

  在我的印象里,对于数组排序,最简单的方法就是使用Arrays.sort(数组a);它也使我理所应当的认为:Arrays这个类完全实现这个sort的方法,而实际上并非如此,先看下sort的原型:public static <T extends Comparable<? super T>> void sort(List<T> list),蓝色部分即是说你要排序的这个集合元素已经实现了接口Comparable,等等,我好像没有实现任何Comparable接口,事实上,String类和所有的包装类对象在java API中已经实现了这个接口。参考API:

      public final class String extends implements , <>,

      如果你尝试为没有实现Comparable接口的元素排序,编译器会出错:The generic method sort(List<T>) of type Collections is not applicable for the arguments(ArrayList<你刚编写的类>). The inferred type 你刚编写的类is not a valid substitute for the bounded parameter <T extends Comparable<? super T>>。

        此接口强行对实现它的每个类的对象进行整体排序。此排序被称为该类的自然排序,类的 compareTo 方法被称为它的自然比较方法。实现此接口的对象列表(和数组)可以通过Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射表中的键或有序集合中的元素,无需指定比较器。对于类C的每一个 e1 和 e2 来说,当且仅当 (e1.compareTo((Object)e2) == 0) 与 e1.equals((Object)e2) 具有相同的布尔值时,类 C 的自然排序才叫做与 equals 一致。注意,null 不是任何类的实例,即使 e.equals(null) 返回 false,e.compareTo(null) 也会抛出 NullPointerException。强烈推荐(虽然不是必需的)使自然排序与 equals 一致。这是因为在使用其自然排序与 equals 不一致的元素(或键)时,没有显式比较器的有序集合(和有序映射表)行为表现“怪异”。尤其是,这样的有序集合(或有序映射表)违背了根据 equals 方法定义的集合(或映射表)的常规协定。例如,如果将两个键 a 和 b 添加到一个没有使用显式比较器的有序集合中,使得 (!a.equals((Object)b) && a.compareTo((Object)b) == 0),则第二个 add 操作返回 false(有序集合的大小没有增加),因为从有序集合的角度来看,a 和 b 是等效的。   实现了comparable接口的类,其对象可以作为SortedMap的key,或者SortedSet的元素。

       我们只能在类中实现compareTo()一次,不可能说经常来修改类的代码实现自己想要的排序,因此如果要以不同于compareTo()方法中指定的顺序排序我们的类对象,那么该怎么办呢?大家一定注意到Collections.sort()有一个重载版本。 public static <T> void sort(List<T> list, Comparator<? super T> c)根据指定比较器产生的顺序对指定列表进行排序。此列表内的所有元素都必须可使用指定比较器相互比较(也就是说,对于列表中的任意 e1 和 e2 元素,c.compare(e1, e2) 不得抛出 ClassCastException)。此排序被保证是稳定的:不会因调用 sort 而对相等的元素进行重新排序。 Comparator<? super T> c 第二个参数是实现了接口Comparator的实例对象。注意这两者的区别,Comparable 接口的方法:public int compareTo(T o),绝不会是Object; 只有一个参数,而Comparator接口却有两个参数:int compare(T o1, T o2);

     Comparator 可以通过 Collections类的reverseOrder()这个方法强势返回对Comparable排序的倒转,如:Arrays.sort(a, Collections.reverseOrder()); 如果是String,就是按照逆字典顺序进行排序。

      这两个接口和集合类本身无关,但通常和集合内的元素有关,因为集合的排序要用到它们中的方法。一个类的实例要想实现排序,必须实现Comparable,或者提供相应的Comparator。说清楚这两者的来历与大概的作用,以及共同点,那么再说下他们两者的区别。只是Comparable(可比较的)是在集合内部定义的方法实现的排序,比如,两个人要比较身高,分辨高矮是人类固有的能力,两个人只要站到一起就能分出谁高谁矮。Comparator(比较器)是在集合外部实现的排序。所以,如想实现排序,就需要在集合外定义Comparator接口的方法compare()或在集合内实现Comparable接口的方法compareTo(),Comparable是一个对象本身就已经支持自比较所需要实现的接口(如String Integer自己就可以完成比较大小操作)而Comparator是一个专用的比较器,当这个对象不支持自比较或者自比较函数不能满足你的要求时,你可以写一个比较器来完成两个对象之间大小的比较。还有个地方就是,实现Comparable只能定义一种比较方法,但是有时候会对一个集合进行不同的排序方法,此时就可以提供别各种各样的Comparator来对集合排序,而对于要排序的元素不需要更改,所以我觉得Comparator提供了更多的灵活性。使用这种策略来比较时,如何进行比较和两个对象本身无关,而是由第三者(即比较器)来完成的。只要实现Comparator接口,任何一个对象都可能成为一个“比较器”,但比较器并不是比较自己的实例,而是比较另外两个对象,比较器在这里充当“仲裁者”的角色,这也就是为什么compare()方法需要两个参数。比如,两个人要比较谁智商更高,靠他们自身无法进行,这时要借助一个比较器(比如,智商测试题)。那么先看一个非常简单易懂的示例:

import java.util.*;
public class Pockets {
   public static void main(String[] args) {
      String[] sa = {"nickel", "button", "key", "lint"};
      Sorter s = new Sorter();    //新建一个排序器
      for(String s2: sa){ System.out.print(s2 + " "); }   
      Arrays.sort(sa, s);    //这个排序器s是与sa这个类对象是相关。
      System.out.println();
      for(String s2:sa){ System.out.print(s2+" "); }
     }
   static class Sorter implements Comparator<String>{  //注意红色的部分就是你要去排序的类,如果你文不对题,硬让这个类去排序一个未知的class Dog的类,会报错:The method sort(T[], Comparator<? super T>) in the type Arrays is not applicable for the arguments (UU[], Pockets.Sorter)
      public int compare(String a, String b){
         return a.compareTo(b);
      }
   }
}

        再在更加复杂的概念上理解这个排序机制,在网上找到一个例子,维护一个简单的员工数据库,每个员工是一个Employee类的实例。Employee类可定义为:

public class Employee {    
    private String num;
    private String name;
    private int age;
    private int salary;
    
    public Employee(String num, String name) {
        this.num = num;
        this.name = name;
    }
    
    public void setName(String newNum) {
        num = newNum;
    }

    public void setAge(int newAge) {
        age = newAge;
    }

    public void setSalary(int newSalary) {
        salary = newSalary;
    }

    public String getNum() {
        return num;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public int getSalary() {
        return salary;
    }

    public String toString() {
        StringBuffer sb = new StringBuffer();
        sb.append("Employee Information:");
        sb.append("\n");
        sb.append("Number:");
        sb.append(getNum());
        sb.append("\n");
        sb.append("Name:");
        sb.append(getName());
        sb.append("\n");
        sb.append("Age:");
        sb.append(getAge());
        sb.append("\n");
        sb.append("Salary:");
        sb.append(getSalary());
        sb.append("\n");
        return sb.toString();
    }
}

EmployeeDatabase类创建Employee类的实例,并把它们存入集合:
Java code

import java.util.*;

public class EmployeeDatabase {    
    public static void main(String[] args) {        
        List<Employee> allEmployees = new ArrayList<Employee>();
        
        Employee employee1 = new Employee("AAA", "Barack Omaba");
        employee1.setAge(50);
        employee1.setSalary(9999);
        allEmployees.add(employee1);
        
        Employee employee2 = new Employee("BBB", "George Bush");
        employee2.setAge(60);
        employee2.setSalary(5999);
        allEmployees.add(employee2);
        
        System.out.println(allEmployees);

    }

}

       现在,你需要检索所有员工,并让他们按一定顺序显示(比如按年龄递增),这时需要用到Collections.sort()方法。Collections.sort()有两种策略:一种是让集合元素本身实现Comparable接口,另一种是使用用户提供的比较器(即Comparator)。使用第一种策略时,必须修改元素类的定义,让它实现Comparable,因此,你必须把Employee类修改为:

public class Employee implements Comparable<Employee> {    
    public int compareTo(Employee another) {
        return getAge() - another.getAge();
    }    
    // 其余部分不变
}

      说明一下,因为compareTo()方法约定:本对象大于另一个对象时,返回大于0的整数,小于时返回小于0的整数,等于时返回0。所以,可以直接返回两者年龄的差,来实现按年龄比较。这样就可以在main()方法中使用Collections.sort(allEmployees);来对员工按年龄排序了。但是,这种排序是非常不灵活的:第一,需要修改集合元素类Employee,而很多情况下,我们没有办法修改公共的类。第二,没有办法实现多种方式排序,如按编号,按姓名,按薪水等等。这时需要使用另一种策略,即Comparator。Comparator使用其compare()方法返回的整数来比较两个对象,规则和compareTo()一样。如同样实现年龄比较,使用Comparator时,无需修改Employee类,可以在排序的时候定义相应的比较器。使用Collections.sort()方法的另一个版本:
Collections.sort(allEmployees, new Comparator<Employee>() {
    public int compare(Employee one, Employee another) {
        return one.getAge() - another.getAge();
    }
});

这里使用了匿名内部类,实际上相当于先定义一个比较器类,如:

class EmployeeComparator implements Comparator<Employee> {    
    public int compare(Employee one, Employee another) {
        return one.getAge() - another.getAge();
    }
    
}

再使用:
Collections.sort(allEmployees, new EmployeeComparator());
可以看到,比较器完全独立于元素类Employee,因此可以非常方便地修改排序规则。你还可以定义一系列比较器,供排序时选择使用,如:

// 按薪水升序
class EmployeeSalaryAscendingComparator implements Comparator<Employee> {    
    public int compare(Employee one, Employee another) {
        return one.getSalary() - another.getSalary();
    }
    
}

// 按薪水降序
class EmployeeSalaryDescendingComparator implements Comparator<Employee> {
    
    public int compare(Employee one, Employee another) {
        return another.getSalary() - one.getSalary();
    }
    
}

相应的使用方法如:
Collections.sort(allEmployees, new EmployeeSalaryAscendingComparator());
Collections.sort(allEmployees, new EmployeeSalaryDescendingComparator());

       使用Comparator时,元素类无需实现Comparable,因此我们保持最初版本的Employee,但实际应用中,可以用Comparable的compareTo()方法来定义默认排序方式,用Comparator定义其他排序方式。实现其compare(T o1,T o2)有一个约定,就是 a.compare(b) == 0 和a.equals(b)要有相同的boolean结果。


 此篇博客整理自网络

  

  Collections and Collection

  Collection是一个集合接口,Collections是一个包装类(或帮助类),这是二者最明显的区别。Collections提供了一 些static方法来对 Collection对象进行处理,比如:对Collection对象的复制、添加元素、修改元素、对元素进行排序、交换俩个元素的位置、取 Collection的子集等等操作。另外Collection是j2sdk中集合框架的根接口,所有的其他特殊类型的结合接口或者类都直接或间接的实现 了这个接口。

  java.util.Collections   API >>

复制代码
 1 public interface Collection<E> extends Iterable<E> { 2     int size(); 3     boolean isEmpty(); 4     boolean contains(Object o); 5     Iterator<E> iterator(); 6     Object[] toArray(); 7     <T> T[] toArray(T[] a); 8     boolean add(E e); 9     boolean remove(Object o);10     boolean containsAll(Collection<?> c);11     boolean addAll(Collection<? extends E> c);12     boolean removeAll(Collection<?> c);13     boolean retainAll(Collection<?> c);14     void clear();15     boolean equals(Object o);16     int hashCode();17 }
复制代码

 

  java.util.Collections   API >>

1 public class Collections {2   //注意Collections只是Collection集合接口的一个帮助类,并没有implements Collection  3   ... ...  4 }

 

  Comparator and Comparable 

  Comparator和Comparable都是接口,俩者之间可以说没什么关系。Comarator位于包java.util下,而Comparable位于包 java.lang下。通俗的说:Comparator是一个比较器,相当于一种工具,它定义了俩个方法,分别是 int compare(T o1, T o2)和 boolean (Object obj),所以你可以通过实现这个接口来定义你所有需要的比较器,如比较俩个人的大小,对象人有姓名、性别、年龄等属性,你在定义这个比较器的时候就可以在compare方法中对传入的俩个参数(person1,person2)的年龄进行大小的比较,从而确定这俩个人的大小。 
  Comparable接口是你要是定义的类要实现的一个接口(如果这个类的实例需要和同一类的别的实例比较大小,而这个大小关系你希望是你自己定义的),它只提供了 int compareTo(T o)方法,也就是说假如我定义了一个Person类,这个类实现了 Comparable接口,那么当我实例化Person类的person1后,我想比较person1和一个现有的Person对象person2的大小时,我就可以这样来调用:person1.comparTo(person2),通过返回值就可以判断了;而此时如果你定义了一个 PersonComparator(实现了Comparator接口)的话,那你就可以这样:PersonComparator comparator= new PersonComparator();comparator.compare(person1,person2);。

  java.lang.Comparable API >>

1 public interface Comparable<T> {2     public int compareTo(T o);3 }
    此接口强行对实现它的每个类的对象进行整体排序。此排序被称为该类的自然排序,类的 compareTo 方法被称为它的自然比较方法。
  实现此接口的对象列表(和数组)可以通过 Collections.sort(和 Arrays.sort)进行自动排序。实现此接口的对象可以用作有序映射表中的键或有序集
合中的元素,无需指定比较器。


 java.util.Comparator API >>

1 public interface Comparator<T> {2     int compare(T o1, T o2);3     boolean equals(Object obj);4 }

   比较函数强行对某些对象 collection 进行整体排序。可以将 Comparator 传递给 sort 方法(如 Collections.sort),从而允许在排序顺序上实现精确控制。还可以使用 Comparator 来控制某些数据结构(如 TreeSet 或 TreeMap)的顺序。

 

  当需要排序的集合或数组不是单纯的数字类型的时候,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。

  Comparator和Comparable的区别如下:

Comparable用在对象本身,说明这个对象是可以被比较的,也就是说可以被排序的。String和Integer之所以可以比较大小,是因为它们都实现了Comparable接口,并实现了compareTo()方法)。

Comparator用在对象外面,相当于定义了一套排序算法来排序。

  
   java.lang.String  API >>


 

复制代码
 1 public final class String implements java.io.Serializable, Comparable<String>, CharSequence{
    ... ...
    //String和Integer之所以可以比较大小,是因为它们都实现了Comparable接口,并实现了compareTo()方法
2 public int compareTo(String anotherString) { 3 int len1 = count; 4 int len2 = anotherString.count; 5 int n = Math.min(len1, len2); 6 char v1[] = value; 7 char v2[] = anotherString.value; 8 int i = offset; 9 int j = anotherString.offset;10 11 if (i == j) {12 int k = i;13 int lim = n + i;14 while (k < lim) {15 char c1 = v1[k];16 char c2 = v2[k];17 if (c1 != c2) {18 return c1 - c2;19 }20 k++;21 }22 } else {23 while (n-- != 0) {24 char c1 = v1[i++];25 char c2 = v2[j++];26 if (c1 != c2) {27 return c1 - c2;28 }29 }30 }31 return len1 - len2;32 }33 }
复制代码

 

  下面通过具体的例子来理解Comparator和Comparable的区别:

   Comparator实例如下:

 

复制代码
 1 class Name implements Comparable<Name>{   2     public String firstName,lastName;   3     public Name(String firstName,String lastName){   4         this.firstName=firstName;   5         this.lastName=lastName;   6     }   7     //实现接口  8     public int compareTo(Name o) {            9         int lastCmp=lastName.compareTo(o.lastName);  10         return (lastCmp!=0?lastCmp:firstName.compareTo(o.firstName));  11     }    12     //便于输出测试  13     public String toString(){                14         return firstName+" "+lastName;  15     }  16 } 
复制代码

 

复制代码
 1 public class NameSort {   2      public static void main(String[] args) {   3          Name nameArray[] = {   4             new Name("John", "Lennon"),   5             new Name("Karl", "Marx"),   6             new Name("Groucho", "Marx"),   7             new Name("Oscar", "Grouch")   8         };  9         Arrays.sort(nameArray);  10         System.out.println(Arrays.toString(nameArray));;  11     }  12 } 
复制代码

  运行结果:[Oscar Grouch, John Lennon, Groucho Marx, Karl Marx]
  Comparable实例如下:

 

复制代码
 1 public  class  User implements  Comparable<Object> {   2     private  String id;   3     private  int  age;   4    5     public  User(String id, int  age) {   6         this .id = id;   7         this .age = age;   8     }   9   10     public  int  getAge() {  11         return  age;  12     }  13     public  void  setAge(int  age) {  14         this .age = age;  15     }  16     public  String getId() {  17         return  id;  18     }  19     public  void  setId(String id) {  20         this .id = id;  21     }  22     23     public int compareTo(Object o) {24         return this.age-((User)o).getAge();25     }26   27     /**  28      * 测试方法  29      */   30     public static void main(String[] args){  31         User[] users = new User[]{new User("a", 30),new User("b", 20 )};  32         Arrays.sort(users);  33         for  (int  i = 0 ; i < users.length; i++) {  34             User user = users[i];  35             System.out.println(user.getId() + " "  + user.getAge());  36         }  37     }  38 }
复制代码

 

  运行结果:b 20
       a 30