代码即财富之我学Java比较器(7)

java.util包中Arrays类作为一个工具类,提供了大量用于数组操作的静态方法,其中包括数组二分查找,对象数组的比较以及对象数组的排序等功能,供用户方便使用。例如使用下面的例子对两个int数组进行比较如下:

import java.util.*;
public class TestCompare{
 public static void main(String[] args) {
  int a[] = {1,2,3,4,5,6};
  int b[] = {5,2,4,1,6,3};
  Arrays.sort(b);
  System.out.println(Arrays.equals(a,b));
 }
}

答案返回true,如果将int更改其包装类Integer,再试一下结果还是一样的,可以排序和比较成功。如果换成我们自己定义的类呢。下面是自定义的Student类,包含名字,年龄,成绩属性,现在希望使用Arrays对Student对象数组进行排序,又会怎样呢?

class Student{
 private String name = null;
 private int age = 0;
 private double score = 0.0;
 public Student(){
 }
 public Student(String name,int age,double score){
  this.name = name;
  this.age = age;
  this.score = score;
 }
 public String toString(){
  return "name=" + this.name + " age=" + this.age + " score=" + this.score;
 }
}

然后在主函数中定义一个Student对象数组,并对其进行比较,输出。

  Student stu[] = {new Student("zhangsan",10,88.5),new Student("lisi",12,92.3),
  new Student("wangwu",11,85.6),new Student("zhaoliu",12,94.5)};
  System.out.println("before sort:");
  for(int i=0;i<stu.length;i++){
   System.out.println(stu[i]);
  }
  Arrays.sort(stu);

比较之前,可以正常输出对象数组信息,比较没有成功返回结果,而是打印出如下信息:

before sort:
name=zhangsan age=10 score=88.5
name=lisi age=12 score=92.3
name=wangwu age=11 score=85.6
name=zhaoliu age=12 score=94.5
Exception in thread "main" java.lang.ClassCastException: Student cannot be cast to java.lang.Comparable
 at java.util.ComparableTimSort.countRunAndMakeAscending(Unknown Source)
 at java.util.ComparableTimSort.sort(Unknown Source)
 at java.util.ComparableTimSort.sort(Unknown Source)
 at java.util.Arrays.sort(Unknown Source)
 at TestCompare.main(TestCompare.java:18)

为什么会出现以上类型转换异常呢,再回过头来看看Arrays的sort方法是如何支持对象数组的比较的。

public static void sort([] a)

Sorts the specified array of objects into ascending order, according to the natural ordering of its elements. All elements in the array must implement the Comparable interface. Furthermore, all elements in the array must be mutually comparable (that is, e1.compareTo(e2) must not throw a ClassCastException for any elements e1 and e2 in the array).

从以上描述可以看出,Arrays的sort方法默认对数组中对象以升序排序,以一个自然顺序,那么这个自然顺序的定义必须使用Comparable接口来完成,即如果要实现对象数组的排序,则此类必须实现Comparable接口,并覆写compareTo方法。下面通过改造Student类,使其实现Comparable接口,并覆写compareTo方法如下:

class Student implements Comparable<Student> {
 private String name = null;
 private int age = 0;
 private double score = 0.0;
 public Student(){
 }
 public Student(String name,int age,double score){
  this.name = name;
  this.age = age;
  this.score = score;
 }
 public int compareTo(Student stu){
  if(this.score > stu.score){
   return 1;
  }else if(this.score < stu.score){
   return -1;
  }else{
   return 0;
  }
 }
 public String toString(){
  return "name=" + this.name + " age=" + this.age + " score=" + this.score;
 }
}

再次在主函数中使用Arrays的sort对Student数组进行排序,看有什么改变:

   before sort:
name=zhangsan age=10 score=88.5
name=lisi age=12 score=92.3
name=wangwu age=11 score=85.6
name=zhaoliu age=12 score=94.5
after sort:
name=wangwu age=11 score=85.6
name=zhangsan age=10 score=88.5
name=lisi age=12 score=92.3
name=zhaoliu age=12 score=94.5

程序成功实现了成绩的升序排序,要实现降序,可以在compareTo方法中对调1和-1即可,可以发现Comparable接口支持泛型,可以帮助我们进行类型检查。实际上这种Comparable接口的 compareTo方法有点类似于二叉排序树,下面就Comparable接口定义一个二叉排序树,如下:(实际上这部分内容属于数据结构,但是从此例子可以清晰发现Comparable与二叉排序树的一致性)

class BinarrySortTree{
 private class Node{
  private Comparable data;
  private Node left;
  private Node right;
  public Node(Comparable data){
   this.data = data;
  }
  public void addNode(Node newNode){
   if(newNode.data.compareTo(this.data) < 0){//left tree
    if(this.left == null){
     this.left = newNode;
    }else{
     this.left.addNode(newNode);
    }
   }else if(newNode.data.compareTo(this.data) > 0){//right tree
    if(this.right == null){
     this.right = newNode;
    }else{
     this.right.addNode(newNode);
    }
    
   }
  }
  public void printNode(){
   if(this.left != null){
    this.left.printNode();
   }
   System.out.println(this.data);
   if(this.right != null){
    this.right.printNode();
   }
  }
 }
 private Node root = null;
 public void add(Comparable data){
  Node newNode = new Node(data);
  if(root == null){
   root = newNode;
  }else{
   root.addNode(newNode);
  }
 }
 public void print(){
  this.root.printNode();
 }
}

在主函数中添加节点,并进行中序遍历输出:

BinarrySortTree tree = new BinarrySortTree();
  for(int i=0;i<5;i++){
   tree.add(5-i);
  }
  tree.print();

还有一种支持对象比较的方式,属于一种补救方式,比如在大型项目中,我所设计的类已经定义打包好了,不希望再对它进行任何修改,现在为了要使此类具有可比较特性,还有一种补救方法就是在类外部定义一个特定的比较器,降低类的耦合度。但此种方法不推荐使用。

class Worker{
 private String name = null;
 private int age = 0;
 private double salary = 0.0;
 public String getName() {
     return this.name;
 }
 public int getAge() {
     return this.age;
 }
 public double getSalary() {
     return this.salary;
 }
 public void setName(String name) {
     this.name = name;
 }
 public void setAge(int age) {
     this.age = age;
 }
 public void setSalary(double salary) {
     this.salary = salary;
 }
 public Worker(){
 }
 public Worker(String name, int age, double salary){
  this.name = name;
  this.age = age;
  this.salary = salary;
 }
 public boolean equals(Object o){
  if(o == null)
   return false;
  if(!(o instanceof Worker))
   return false;
  Worker worker = (Worker)o;
  if(this.name.equals(worker.name) && this.age == worker.age 
  && this.salary == worker.salary)
   return true;
  else
   return false;
 }
 public String toString(){
  return "name=" + this.name + " age=" + this.age + " salary=" + this.salary;
 }
}
class WorkerComparator implements Comparator<Worker>{
 public boolean equals(Object obj){
  return this.equals(obj);
 }
 public int compare(Worker o1, Worker o2){
  if(o1.getSalary() < o2.getSalary()){
   return -1;
  }else if(o1.getSalary() > o2.getSalary()){
   return 1;
  }else{
   return 0;
  }
 }
}

定义外在的WorkerComparator比较器 ,需要实现该接口的equals和compare方法,equals一般在要支持比较的那个类内部定义完善。使用上述Comparator比较器。

Worker worker[] = {new Worker("peter",30,12000),new Worker("tony",25,15000),
  new Worker("paul",31,13000),new Worker("danel",35,25000)};

  System.out.println("before sort:");
  for(int i=0;i<worker.length;i++){
   System.out.println(worker[i]);
  }
  Arrays.sort(worker,new WorkerComparator());
  System.out.println("after sort:");
  for(int i=0;i<worker.length;i++){
   System.out.println(worker[i]);
  }

输出如下:

before sort:
name=peter age=30 salary=12000.0
name=tony age=25 salary=15000.0
name=paul age=31 salary=13000.0
name=danel age=35 salary=25000.0
after sort:
name=peter age=30 salary=12000.0
name=paul age=31 salary=13000.0
name=tony age=25 salary=15000.0
name=danel age=35 salary=25000.0

说明这种方式也支持对象数组的比较,但还是推荐大家使用定义类的同时,如果觉得此类有必要支持比较,就实现Comparable接口,毕竟Comparator是作为一种补救出现。

从网上看到的一些关于这两个接口的比较:

    1)Comparable接口位于java.lang包中,Comparator接口位于java.util包中

    2)Comparator属于自然顺序比较排序,对于TreeSet或者TreeMap中的Key可以实现有序映射,而不需要制定比较器

    3)Comparator属于一种策略模式,即不修改原对象,使用策略对象来达到修改原对象行为的目的

    4)Comparable接口是集合内部定义,自己完成比较;Comparator是外部程序实现比较。

转载于:https://my.oschina.net/u/1258323/blog/474452

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值