当前位置: 移动技术网 > IT编程>开发语言>Java > java 中Comparable与Comparator详解与比较

java 中Comparable与Comparator详解与比较

2019年07月22日  | 移动技术网IT编程  | 我要评论
java 中comparable与comparator详解 今天查看treemap的源码,发现其键必须是实现comparable或者comparator的接口时产生了一些

java 中comparable与comparator详解

今天查看treemap的源码,发现其键必须是实现comparable或者comparator的接口时产生了一些兴趣,比如在treemap中的put方法分别对comparable和comparator接口分别进行处理。那么疑问就来了,comparable和comparator接口的区别是什么,java中为什么会存在两个类似的接口?

  comparable和comparator接口都是用来比较大小的,首先来看一下comparable的定义:

 package java.lang;
import java.util.*;
public interface comparable<t> {
  public int compareto(t o);
}

  comparator的定义如下:

package java.util;
public interface comparator<t> {
  int compare(t o1, t o2);
  boolean equals(object obj);
}

  comparable对实现它的每个类的对象进行整体排序。这个接口需要类本身去实现(这句话没看懂?没关系,接下来看个例子就明白了)。若一个类实现了comparable 接口,实现 comparable 接口的类的对象的 list 列表 ( 或数组)可以通过 collections.sort(或 arrays.sort)进行排序。此外,实现 comparable 接口的类的对象 可以用作 “有序映射 ( 如 treemap)” 中的键或 “有序集合 (treeset)” 中的元素,而不需要指定比较器。

  举例(类person1实现了comparable接口)

package collections;

public class person1 implements comparable<person1>
{
  private int age;
  private string name;

  public person1(string name, int age)
  {
    this.name = name;
    this.age = age;
  }
  @override
  public int compareto(person1 o)
  {
    return this.age-o.age;
  }
  @override 
  public string tostring()
  {
    return name+":"+age;
  }
}

  可以看到person1实现了comparable接口中的compareto方法。实现comparable接口必须修改自身的类,即在自身类中实现接口中相应的方法。

  测试代码:

 person1 person1 = new person1("zzh",18);
    person1 person2 = new person1("jj",17);
    person1 person3 = new person1("qq",19);

    list<person1> list = new arraylist<>();
    list.add(person1);
    list.add(person2);
    list.add(person3);

    system.out.println(list);
    collections.sort(list);
    system.out.println(list);

  输出结果:

[zzh:18, jj:17, qq:19]
[jj:17, zzh:18, qq:19]

  如果我们的这个类无法修改,譬如string,我们又要对其进行排序,当然string中已经实现了comparable接口,如果单纯的用string举例就不太形象。对类自身无法修改这就用到了comparator这个接口(策略模式)。

public final class person2
{
  private int age;
  private string name;

  public person2(string name, int age)
  {
    this.name = name;
    this.age = age;
  }

  @override 
  public string tostring()
  {
    return name+":"+age;
  }

  //getter and setter方法省略....
}

  如类person2,这个类已经固定,无法进行对其类自身的修改,也修饰词final了,你也别想继承再implements comparable,那么此时怎么办呢?在类的外部使用comparator的接口。如下测试代码:

 person2 p1 = new person2("zzh",18);
    person2 p2 = new person2("jj",17);
    person2 p3 = new person2("qq",19);
    list<person2> list2 = new arraylist<person2>();
    list2.add(p1);
    list2.add(p2);
    list2.add(p3);
    system.out.println(list2);
    collections.sort(list2,new comparator<person2>(){

      @override
      public int compare(person2 o1, person2 o2)
      {
        if(o1 == null || o2 == null)
          return 0;
        return o1.getage()-o2.getage();
      }

    });
    system.out.println(list2);

  输出结果:

[zzh:18, jj:17, qq:19]
[jj:17, zzh:18, qq:19] 

  这里(public static <t> void sort(list<t> list, comparator<? super t> c) )采用了内部类的实现方式,实现compare方法,对类person2的list进行排序。

  再譬如博主遇到的真实案例中,需要对string进行排序,且不区分大小写,我们知道string中的排序是字典排序,譬如:a a d排序之后为a d a,这样显然不对,那么该怎么办呢?同上(下面代码中的list是一个string的list集合):

 collections.sort(list, new comparator<string>()
    {
      @override
      public int compare(string o1, string o2)
      {
        if(o1 == null || o2 == null)
          return 0;
        return o1.touppercase().compareto(o2.touppercase());
      }
    });

  这样就可以实现不区分大小进行排序string的集合了,是不是很方便~

  细心的同学可能会有疑问,明明在comparator接口中定义了两个方法,为什么继承的时候只实现了一个方法,难道要颠覆我对java接口常识的理解了嚒?

  实际上,我们知道当一个类没有显式继承父类的时候,会有一个默认的父类,即java.lang.object,在object类中有一个方法即为equals方法,所以这里并不强制要求实现comparator接口的类要实现equals方法,直接调用父类的即可,虽然你显式的实现了equals()方法 will be a better choice~

  在《effective java》一书中,作者joshua bloch推荐大家在编写自定义类的时候尽可能的考虑实现一下comparable接口,一旦实现了comparable接口,它就可以跟许多泛型算法以及依赖于改接口的集合实现进行协作。你付出很小的努力就可以获得非常强大的功能。

  事实上,java平台类库中的所有值类都实现了comparable接口。如果你正在编写一个值类,它具有非常明显的内在排序关系,比如按字母顺序、按数值顺序或者按年代顺序,那你就应该坚决考虑实现这个接口。

  compareto方法不但允许进行简单的等同性进行比较,而且语序执行顺序比较,除此之外,它与object的equals方法具有相似的特征,它还是一个泛型。类实现了comparable接口,就表明它的实例具有内在的排序关系,为实现comparable接口的对象数组进行排序就这么简单: arrays.sort(a);

  对存储在集合中的comparable对象进行搜索、计算极限值以及自动维护也同样简单。列如,下面的程序依赖于string实现了comparable接口,它去掉了命令行参数列表中的重复参数,并按字母顺序打印出来:

public class wordlist{
  public static void main(string args[]){
    set<string> s = new treeset<string>();
    collections.addall(s,args);
    system.out.println(s);
  }
}

  comparable 是排序接口;若一个类实现了 comparable 接口,就意味着 “该类支持排序”。而 comparator 是比较器;我们若需要控制某个类的次序,可以建立一个 “该类的比较器” 来进行排序。

  前者应该比较固定,和一个具体类相绑定,而后者比较灵活,它可以被用于各个需要比较功能的类使用。可以说前者属于 “静态绑定”,而后者可以 “动态绑定”。

  我们不难发现:comparable 相当于 “内部比较器”,而 comparator 相当于 “外部比较器”。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

如您对本文有疑问或者有任何想说的,请 点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网