A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

© 孟庆全 初级黑马   /  2012-9-20 00:20  /  1474 人查看  /  2 人回复  /   0 人收藏 转载请遵从CC协议 禁止商业使用本文

根据一个泛型比较器Comparator<T>对象,如何获取允许比较对象的类型

2 个回复

正序浏览
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>{  
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结果。
回复 使用道具 举报
/**
* 获取可比较的元素类型
*/
private Class<?> getComparableTypeType(Comparator<T> comparator) {
    Type[] types = comparator.getClass().getGenericInterfaces();
    for (Type type : types) {
        if (type instanceof ParameterizedType) {
            ParameterizedType ptype = (ParameterizedType) type;
            if (Comparator.class.equals(ptype.getRawType())) {
                Type cmpType = ptype.getActualTypeArguments()[0];
                if (cmpType instanceof ParameterizedType) {
                    return (Class<?>) ((ParameterizedType) cmpType).getRawType();
                } else {
                   return (Class<?>) ptype.getActualTypeArguments()[0];
                }
            }
        }
    }
    return Object.class;
}
回复 使用道具 举报
您需要登录后才可以回帖 登录 | 加入黑马