一、简介
本文主要讲解Comparable接口以及Comparator接口的比较原理以及在实际场景的一些使用方法,以及两者的区别。
二、Comparable接口
Comparable是一个排序接口,如果一个类实现了Comparable接口,并且实现了compareTo()方法,那么这个类就支持排序。Comparable接口是一个内比较器,这些类可以与自身进行比较。实现了Comparable接口的类如果加入到List等容器中后,会进行自动排序。这主要是由Comparable接口中compareTo(T o)方法决定的,定义如下:
public int compareTo(T o);
compareTo()方法主要有三种返回值:
【a】比较象大于被比较者,返回正整数;
【b】比较者等于被比较者,返回0;
【c】比较者小于被比较者,返回负整数;
其实JDK提供了一些类也实现了Comparable接口,如String、Date、Integer、Character等。下面我们对一些常见的内置引用类型实现了Comparable接口的类的比较原理做一些讲解:
【1】String类:根据字符Unicode进行比较,依次比较各个字符,返回第一个不相同的字符的Unicode码之差。如果字符都相同则返回字符串长度之差。
public int compareTo(String anotherString) {
int len1 = value.length;
int len2 = anotherString.value.length;
int lim = Math.min(len1, len2);
char v1[] = value;
char v2[] = anotherString.value;
int k = 0;
while (k < lim) {
//根据字符Unicode进行比较
char c1 = v1[k];
char c2 = v2[k];
if (c1 != c2) {
//返回第一个不相等的Unicode码大小之差
return c1 - c2;
}
k++;
}
//返回两个字符串的长度之差
return len1 - len2;
}
【2】Integer类:根据基本数据类型数值大小进行比较
public static int compare(int x, int y) {
return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
【3】Character类:根据Unicode编码大小进行比较
public static int compare(char x, char y) {
return x - y;
}
【4】Date类:获取相应的毫秒数,再进行大小比较
public int compareTo(Date anotherDate) {
//获取相应的毫秒数,再进行大小比较
long thisTime = getMillisOf(this);
long anotherTime = getMillisOf(anotherDate);
return (thisTime<anotherTime ? -1 : (thisTime==anotherTime ? 0 : 1));
}
下面对内置引用类型做一个简单的测试:
/**
* @Description:c 内置引用数据类型比较原理分析
* @Author: weishihuai
* @Date: 2018/10/18 22:17
*/
public class Test {
public static void main(String[] args) {
//根据基本数据类型数值大小
Integer a = 120;
Integer b = 100;
System.out.println(a.compareTo(b) == -1 ? "a < b" : (a.compareTo(b) == 0) ? "a = b" : "a > b");
//根据Unicode编码大小
Character c1 = 'a';
Character c2 = 'c';
System.out.println(c1.compareTo(c2) < 0 ? "c1 < c2" : (c1.compareTo(c2) == 0) ? "c1 = c2" : "c1 > c2");
//根据字符Unicode进行比较,依次比较各个字符,返回第一个不相同的字符的Unicode码之差。如果字符都相同则返回字符串长度之差。
String s1 = "ad";
String s2 = "adbf";
System.out.println(s1.compareTo(s2) < 0 ? "s1 < s2" : (s1.compareTo(s2) == 0 ? "s1 = s2" : "s1 > s2"));
//获取相应的毫秒数,再进行大小比较
Date d1 = new Date();
Date d2 = new Date(System.currentTimeMillis() + 60 * 1000 * 1000);
System.out.println(d1.compareTo(d2) == -1 ? "d1 < d2" : (d1.compareTo(d2) == 0 ? "d1 = d2" : "d1 > d2"));
}
}
测试结果:
以上是JDK提供的一些已经实现了Comparable接口的引用类型类的讲解,那如果我们想使用自定义的类进行比较,同理,只需要让该类实现Comparable接口即可,重写compareTo()方法。下面我们通过一个示例说明:按发布日期降序、浏览量升序、标题降序排序新闻。
【a】首先,创建一个News新闻类,该类实现Comparable接口,重写compareTo方法:泛型指定News类,当然也可以指定其他类。
/**
* @Description: 新闻类
* @Author: weishihuai
* @Date: 2018/10/17 21:53
* <p>
* Comparable的使用方法: 实现compareTo方法
*/
public class News implements Comparable<News> {
/**
* 浏览量
*/
private int hits;
/**
* 发布时间
*/
private Date publishDate;
/**
* 标题
*/
private String title;
public News() {
}
public News(int hits, Date publishDate, String title) {
this.hits = hits;
this.publishDate = publishDate;
this.title = title;
}
@Override
public int compareTo(News o) {
int result;
//发布日期降序
result = -this.publishDate.compareTo(o.publishDate);
if (result == 0) {
//浏览量升序
result = this.hits - o.hits;
if (result == 0) {
//标题降序
result = -this.title.compareTo(o.title);
}
}
return result;
}
public int getHits() {
return hits;
}
public void setHits(int hits) {
this.hits = hits;
}
public Date getPublishDate() {
return publishDate;
}
public void setPublishDate(Date publishDate) {
this.publishDate = publishDate;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
@Override
public String toString() {
return "News{" +
"hits=" + hits +
", publishDate=" + publishDate +
", title='" + title + '\'' +
'}' + "\n";
}
}
【b】测试News是否能够按指定的规则排序:
public class TestNewsComparable {
public static void main(String[] args) {
List<News> newsList = new ArrayList<>();
newsList.add(new News(100, new Date(), "hello1"));
newsList.add(new News(70, new Date(System.currentTimeMillis() - 1000 * 60 * 60), "hello2"));
newsList.add(new News(110, new Date(System.currentTimeMillis() - 1000 * 60 * 60), "hello4"));
newsList.add(new News(110, new Date(System.currentTimeMillis() - 1000 * 60 * 60), "hello5"));
newsList.add(new News(80, new Date(System.currentTimeMillis() + 1000 * 60 * 60), "hello3"));
System.out.println("排序前: " + Arrays.toString(newsList.toArray()));
Collections.sort(newsList);
System.out.println("排序后: " + Arrays.toString(newsList.toArray()));
}
}
测试结果:
由上图可以看到,News按照我们指定的规则进行了排序,首先按发布日期降序、发布日期相同则按浏览量升序、浏览量相同则按标题降序。
三、Comparator接口
Comparator接口是一个外比较器,如果一个自定义的类想进行比较,而且又没有实现Compable接口,那么我们可以自定义Comparator比较器让这个类也可以进行比较。Comparator接口适用于不想跟自身进行比较的场景,主要实现方法为compare()方法、
int compare(T o1, T o2);
方法有两个参数,分别表示两个不同的对象。返回值同样有三种情况:
【1】o1大于o2,返回正整数
【2】o1等于o2,返回0
【3】o1小于o3,返回负整数
我们都知道JDK提供的String比较是根据字符的Unicode码进行比较,那么我们可以定义一个Comparator比较器使String比较长度来进行排序。
【a】自定义StringComparator,实现compare()方法
/**
* @Description: 内置类String比较器
* @Author: weishihuai
* @Date: 2018/10/18 22:46
*/
public class StringComparator implements Comparator<String> {
@Override
public int compare(String string1, String string2) {
//根据长度比较
return string1.length() - string2.length();
}
}
【b】测试:
public class TestStringComparator {
public static void main(String[] args) {
List<String> stringList = new ArrayList<>();
stringList.add("zhangsan");
stringList.add("lisi");
stringList.add("wangwu");
Collections.sort(stringList, new StringComparator());
System.out.println(Arrays.toString(stringList.toArray()));
}
}
【c】测试结果
可以看到,字符串已经使用长度比较进行了排序。
下面我们同样使用一个示例来说明Comparator接口的使用:
【a】首先定义Goods类
/**
* @Description: 商品类
* @Author: weishihuai
* @Date: 2018/10/17 22:09
*/
public class Goods {
/**
* 商品名称
*/
private String name;
/**
* 浏览量
*/
private int hits;
/**
* 商品价格
*/
private double price;
public Goods() {
}
public Goods(String name, int hits, double price) {
this.name = name;
this.hits = hits;
this.price = price;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHits() {
return hits;
}
public void setHits(int hits) {
this.hits = hits;
}
public double getPrice() {
return price;
}
public void setPrice(double price) {
this.price = price;
}
@Override
public String toString() {
return "Goods{" +
"name='" + name + '\'' +
", hits=" + hits +
", price=" + price +
'}' + "\n";
}
}
【b】根据具体的业务定义相应的业务比较器
这里我们定义两个业务相关的比较器:
(1) 按商品点击量比较Comparator (升序)
/**
* @Description: 商品点击量比较Comparator (升序)
* @Author: weishihuai
* @Date: 2018/10/17 22:12
*/
public class GoodsFitsComparator implements Comparator<Goods> {
@Override
public int compare(Goods o1, Goods o2) {
return o1.getHits() - o2.getHits() > 0 ? 1 : (o1.getHits() - o2.getHits() == 0 ? 0 : -1);
}
}
(2) 按商品价格比较Comparator (降序)
/**
* @Description: 商品价格比较Comparator (降序)
* @Author: weishihuai
* @Date: 2018/10/17 22:12
*/
public class GoodsPriceComparator implements Comparator<Goods> {
@Override
public int compare(Goods o1, Goods o2) {
return -(o1.getPrice() - o2.getPrice() > 0 ? 1 : (o1.getPrice() - o2.getPrice()) == 0 ? 0 : -1);
}
}
【c】测试
/**
* @Description: 测试Comparator接口的使用方法
* @Author: weishihuai
* @Date: 2018/10/17 22:02
*/
public class TestGoodsFitsComparator {
public static void main(String[] args) {
List<Goods> goodsList = new ArrayList<>();
goodsList.add(new Goods("商品1", 100, 2000));
goodsList.add(new Goods("商品2", 110, 1000));
goodsList.add(new Goods("商品3", 10, 3000));
//按价格降序排序
System.out.println("排序前: " + Arrays.toString(goodsList.toArray()));
Collections.sort(goodsList, new GoodsFitsComparator());
System.out.println("排序后: " + Arrays.toString(goodsList.toArray()));
}
}
测试结果:
可见,已经按商品价格降序排序。
/**
* @Description: 测试Comparator接口的使用方法
* @Author: weishihuai
* @Date: 2018/10/17 22:02
*/
public class TestGoodsFitsComparator {
public static void main(String[] args) {
List<Goods> goodsList = new ArrayList<>();
goodsList.add(new Goods("商品1", 100, 2000));
goodsList.add(new Goods("商品2", 110, 1000));
goodsList.add(new Goods("商品3", 10, 3000));
//按价格降序排序
System.out.println("排序前: " + Arrays.toString(goodsList.toArray()));
Collections.sort(goodsList, new GoodsFitsComparator());
System.out.println("排序后: " + Arrays.toString(goodsList.toArray()));
}
}
测试结果:
可以看见,商品已经实现了按点击量升序排序。
四、Comparable与Comparator的比较
1. 实现方法不同
Comparable: compareTo()方法
Comparator:compare()方法
2. Comparator耦合度相对较低,Comparable耦合度相对较高,假如需要增加新的比较规则,对于Comparable接口需要修改实体类的比较规则,而Comparator接口则不需要修改实体类的任何代码,只需增加相应的业务比较器接口即可。
3. Comparable可以跟自身进行比较,Comparator则不适用于跟自身进行比较的场景。
在实际项目中,根据具体的场景选择合适的比较接口即可,并没有说一定要使用哪一个接口好一点。
五、总结
本文是笔者对Comparable接口以及Comparator接口的一些见解以及实践方法,希望对大家有所帮助,仅供大家学习参考,一起学习,一起进步!