当需要排序的集合或数组不是单纯的数字型时,通常可以使用Comparator或Comparable,以简单的方式实现对象排序或自定义排序。
一、Comparator
强行对某个对象collection进行整体排序的比较函数,可以将Comparator传递给Collections.sort或Arrays.sort。
接口方法:
* @return o1小于、等于或大于o2,分别返回负整数、零或正整数。
*/
int compare(Object o1, Object o2);
案例:
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,需要按年龄排序:
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 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.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