Arrays.sort、Comparable与Comparator两个接口的作用

有的时候需要对数组里的element进行排序。当然可以自己编写合适的排序方法,但既然java包里有自带的Arrays.sort排序方法,在数组元素比较少的时候为何不用?

  Sorting an Array 1. 数字排序  int[] intArray = new int[] { 4, 1, 3, -23 };

  Arrays.sort(intArray);

  输出: [-23, 1, 3, 4]

  2. 字符串排序,先大写后小写 String[] strArray = new String[] { "z", "a", "C" };

  Arrays.sort(strArray);

  输出: [C, a, z]

  3. 严格按字母表顺序排序,也就是忽略大小写排序 Case-insensitive sort

  Arrays.sort(strArray, String.CASE_INSENSITIVE_ORDER);

  输出: [a, C, z]

  4. 反向排序, Reverse-order sort

  Arrays.sort(strArray, Collections.reverseOrder());

  输出:[z, a, C]

  5. 忽略大小写反向排序 Case-insensitive reverse-order sort

  Arrays.sort(strArray, String.CASE_INSENSITIVE_ORDER);

  Collections.reverse(Arrays.asList(strArray));

  输出: [z, C, a]

  java初学者最常见的错误思想,就是试图去写一些方法来完成数组的排序功能,其实,数组排序功能,在java的api里面早已实现,我们没有必要去重复制造轮子。

  Arrays类有一个静态方法sort,利用这个方法我们可以传入我们要排序的数组进去排序,因为我们传入的是一个数组的引用,所以排序完成的结果也通过这个引用的来更改数组.对于整数、字符串排序,jdk提供了默认的实现,如果要对一个对象数组排序,则要自己实现java.util.Comparator接口。

  package demo1.client;

  import java.util.Arrays;

  import java.util.Comparator;

  public class ArraySortDemo {

  public void sortIntArray() {

  int[] arrayToSort = new int[] { 48, 5, 89, 80, 81, 23, 45, 16, 2 };

  System.out.println("排序前");

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  // 调用数组的静态排序方法sort

  Arrays.sort(arrayToSort);

  System.out.println("排序后");

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  }

  public void sortStringArray() {

  String[] arrayToSort = new String[] { "Oscar", "Charlie", "Ryan",

  "Adam", "David" };

  System.out.println("排序前");

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  System.out.println("排序后");

  //调用数组的静态排序方法sort

  Arrays.sort(arrayToSort);

  for (int i = 0; i < arrayToSort.length; i++)

  System.out.println(arrayToSort[i]);

  }

  public void sortObjectArray() {

  Dog o1 = new Dog("dog1", 1);

  Dog o2 = new Dog("dog2", 4);

  Dog o3 = new Dog("dog3", 5);

  Dog o4 = new Dog("dog4", 2);

  Dog o5 = new Dog("dog5", 3);

  Dog[] dogs = new Dog[] { o1, o2, o3, o4, o5 };

  System.out.println("排序前");

  for (int i = 0; i < dogs.length; i++) {

  Dog dog = dogs[i];

  System.out.println(dog.getName());

  }

  Arrays.sort(dogs, new ByWeightComparator());

  System.out.println("排序后:");

  for (int i = 0; i < dogs.length; i++) {

  Dog dog = dogs[i];

  System.out.println(dog.getName());

  }

  }

  public static void main(String[] args) {

  ArraySortDemo t = new ArraySortDemo();

  t.sortIntArray();

  t.sortStringArray();

  t.sortObjectArray();

  }

  }

  class Dog {

  private String name;

  private int weight;

  public Dog(String name, int weight) {

  this.setName(name);

  this.weight = weight;

  }

  public int getWeight() {

  return weight;

  }

  public void setWeight(int weight) {

  this.weight = weight;

  }

  public void setName(String name) {

  this.name = name;

  }

  public String getName() {

  return name;

  }

  }

  class ByWeightComparator implements Comparator {

  public final int compare(Object pFirst, Object pSecond) {

  int aFirstWeight = ((Dog) pFirst).getWeight();

  int aSecondWeight = ((Dog) pSecond).getWeight();

  int diff = aFirstWeight - aSecondWeight;

  if (diff > 0)

  return 1;

  if (diff < 0)

  return -1;

  else

  return 0;

  }

  }


在我的印象里,对于数组排序,最简单的方法就是使用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结果。


  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值