天道不一定酬所有勤
但是,天道只酬勤
Hollis出品的全套Java面试宝典不来了解一下吗?

简单介绍Java中Comparable和Comparator

Hollis出品的全套Java面试宝典不来了解一下吗?

ComparableComparator是Java核心API提供的两个接口,从它们的名字中,我们大致可以猜到它们用来做对象之间的比较的。但它们到底怎么用,它们之间有又哪些差别呢?下面有两个例子可以很好的回答这个问题。下面的例子用来比较HDTV的大小。看完下面的代码,相信对于如何使用ComparableComparator会有一个更加清晰的认识。

Comparable

一个实现了Comparable接口的类,可以让其自身的对象和其他对象进行比较。也就是说,同一个类的两个对象之间要想比较,对应的类就要实现Comparable接口,并实现compareTo()方法,代码如下:

class HDTV implements Comparable<HDTV> {
    private int size;
    private String brand;

    public HDTV(int size, String brand) {
        this.size = size;
        this.brand = brand;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }

    @Override
    public int compareTo(HDTV tv) {

        if (this.getSize() > tv.getSize())
            return 1;
        else if (this.getSize() < tv.getSize())
            return -1;
        else
            return 0;
    }
}

public class Main {
    public static void main(String[] args) {
        HDTV tv1 = new HDTV(55, "Samsung");
        HDTV tv2 = new HDTV(60, "Sony");

        if (tv1.compareTo(tv2) > 0) {
            System.out.println(tv1.getBrand() + " is better.");
        } else {
            System.out.println(tv2.getBrand() + " is better.");
        }
    }
}

输出结果:

Sony is better.

Comparator

在一些情况下,你不希望修改一个原有的类,但是你还想让他可以比较,Comparator接口可以实现这样的功能。通过使用Comparator接口,你可以针对其中特定的属性/字段来进行比较。比如,当我们要比较两个人的时候,我可能通过年龄比较、也可能通过身高比较。这种情况使用Comparable就无法实现(因为要实现Comparable接口,其中的compareTo方法只能有一个,无法实现多种比较)。

通过实现Comparator接口同样要重写一个方法:compare()。接下来的例子就通过这种方式来比较HDTV的大小。其实Comparator通常用于排序。Java中的CollectionsArrays中都包含排序的sort方法,该方法可以接收一个Comparator的实例(比较器)来进行排序。

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

class HDTV {
    private int size;
    private String brand;

    public HDTV(int size, String brand) {
        this.size = size;
        this.brand = brand;
    }

    public int getSize() {
        return size;
    }

    public void setSize(int size) {
        this.size = size;
    }

    public String getBrand() {
        return brand;
    }

    public void setBrand(String brand) {
        this.brand = brand;
    }
}

class SizeComparator implements Comparator<HDTV> {
    @Override
    public int compare(HDTV tv1, HDTV tv2) {
        int tv1Size = tv1.getSize();
        int tv2Size = tv2.getSize();

        if (tv1Size > tv2Size) {
            return 1;
        } else if (tv1Size < tv2Size) {
            return -1;
        } else {
            return 0;
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HDTV tv1 = new HDTV(55, "Samsung");
        HDTV tv2 = new HDTV(60, "Sony");
        HDTV tv3 = new HDTV(42, "Panasonic");

        ArrayList<HDTV> al = new ArrayList<HDTV>();
        al.add(tv1);
        al.add(tv2);
        al.add(tv3);

        Collections.sort(al, new SizeComparator());
        for (HDTV a : al) {
            System.out.println(a.getBrand());
        }
    }
}

输出结果:

Panasonic
Samsung
Sony

以上代码就实现了通过自定义一个比较器(Comparator)来实现对一个列表进行排序。

我们也经常会使用Collections.reverseOrder()来获取一个倒序的Comparator。例如:

ArrayList<Integer> al = new ArrayList<Integer>();
al.add(3);
al.add(1);
al.add(2);
System.out.println(al);
Collections.sort(al);
System.out.println(al);

Comparator<Integer> comparator = Collections.reverseOrder();
Collections.sort(al,comparator);
System.out.println(al);

输出结果:

[3,1,2]
[1,2,3]
[3,2,1]

如何选择

简单来说,一个类如果实现Comparable接口,那么他就具有了可比较性,意思就是说它的实例之间相互直接可以进行比较。

通常在两种情况下会定义一个实现Comparator类。

1、如上面的例子一样,可以把一个Comparator的子类传递给Collections.sort()Arrays.sort()等方法,用于自定义排序规则。

2、用于初始化特定的数据结构。常见的有可排序的Set(TreeSet)和可排序的Map(TreeMap

下面通过这两种方式分别创建TreeSet

使用Comparator创建TreeSet

class Dog {
    int size;

    Dog(int s) {
        size = s;
    }
}

class SizeComparator implements Comparator<Dog> {
    @Override
    public int compare(Dog d1, Dog d2) {
        return d1.size - d2.size;
    }
}

public class ImpComparable {
    public static void main(String[] args) {
        TreeSet<Dog> d = new TreeSet<Dog>(new SizeComparator()); // pass comparator
        d.add(new Dog(1));
        d.add(new Dog(2));
        d.add(new Dog(1));
    }
}

这里使用的就是Comparator的第二种用法,定义一个Comparator的子类,重写compare方法。然后在定义HashSet的时候,把这个类的实例传递给其构造函数。这样,再使用add方法向HashSet中增加元素的时候,就会按照刚刚定义的那个比较器的逻辑进行排序。

使用Comparable创建TreeSet

class Dog implements Comparable<Dog>{
    int size;

    Dog(int s) {
        size = s;
    }

    @Override
    public int compareTo(Dog o) {
        return o.size - this.size;
    }
}

public class ImpComparable {
    public static void main(String[] args) {
        TreeSet<Dog> d = new TreeSet<Dog>();
        d.add(new Dog(1));
        d.add(new Dog(2));
        d.add(new Dog(1));
    }
}

这里,定义TreeSet的时候并没有传入一个比较器。但是使用add方法向HashSet中增加的对象是一个实现了Comparable的类的实例。所以,也能实现排序功能。

赞(0)
如未加特殊说明,此网站文章均为原创,转载必须注明出处。HollisChuang's Blog » 简单介绍Java中Comparable和Comparator
Hollis出品的全套Java面试宝典不来了解一下吗?

评论 3

  1. #1

    嗯~o(*[emoji:ffe3]▽[emoji:ffe3]*)o很不错,重新认识了一遍[/给力]

    Feedmo8年前 (2016-09-10)回复
  2. #2

    因为要实现Comparable接口,其中的compareTo方法只能有一个,无法实现多种比较。这句无法理解

    zhuoyuehc5年前 (2018-12-17)回复
    • Comparable只能有一种比较方式,因为compareTo方法只能有一个,不能重载
      而Comparator可以实现多个比较器,根据需要传入不同的比较器可以实现多种比较方式

      sushengbuyu5年前 (2019-03-19)回复

HollisChuang's Blog

联系我关于我