Comparable 和 Comparator比较

有两种方式可以进行集合排序 :

  • 集合中对象的所属类实现了 java.lang.Comparable 接口
  • 给集合指定比较器 java.lang.Comparator 的实现类

1. java.lang.Comparable

public interface Comparable 接口强行对实现它的每个类的对象进行整体排序。 – 自然排序。类的compareTo称为自然比较方法。

接口的作用

若一个类实现了Comparable 接口,实现 Comparable 接口的类对象的 List 列表 ( 或数组)可以通过 Collections.sort(或 Arrays.sort)进行排序。

此外,实现 Comparable 接口的类的对象 可以用作 “有序映射 ( 如 TreeMap)” 中的键或 “有序集合 (TreeSet)” 中的元素,而不需要指定比较器。

实现的方式

利用Comparable接口创建自己的类的排序顺序,只是实现comparaTo方法的问题。
通常就是依赖几个数据成员的自然排序。同时类也应该覆盖equals()和hashCode() 以确保两个相等的对象返回同一个哈希码。

Comparable接口只有一个方法,compareTo(Object obj),定义如下:

1
2
3
public interface Comparable<T> {
public int compareTo(T o);
}

通常需要覆写 compareTo 方法实现排序规则的应用 :int compareTo(Object o): 比较当前实例对象与对象 o ,如果位于对象 o 之前,返回负值,如果两个对象在排序中位置相同,则返回 0 ,如果位于对象 o 后面,则返回正值.

下表展示了几种基本类型的自然排序。虽然一些类共享同一种自然排序,但只有相互可比的类才能排序。

排序(越大排在后面)
Integer 按数字大小排序
Long 按数字大小排序
Byte 按数字大小排序
Double 按数字大小排序
Character Unicode 值的数字大小排序
String 按字符串中字符 Unicode 值排序

举个例子:一种商品(商品名,销售量,生产日期),根据生产日期降序 销售量升序 商品名称降序

思路:首先按照日期降序,如果日期相同 按照销售量升序,如果销售量相同,按周商品的名称降序

  • 创建需要比较的对象的java bean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
public class ComparableExample implements Comparable<ComparableExample> {
private String title; // 名称
private int hits; // 销售量
private Date pubTime; // 日期
public ComparableExample() {
}
public ComparableExample(String title, int hits, Date pubTime) {
super();
this.title = title;
this.hits = hits;
this.pubTime = pubTime;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getHits() {
return hits;
}
public void setHits(int hits) {
this.hits = hits;
}
public Date getPubTime() {
return pubTime;
}
public void setPubTime(Date pubTime) {
this.pubTime = pubTime;
}
// 时间降序(越大排在前面) 点击量升序(越大排在后面) 标题降序
@Override
public int compareTo(ComparableExample o) {
int result = 0;
// 按照生产时间降序
result = -this.pubTime.compareTo(o.pubTime);
if (0 == result) {// 如果生产时间相同 就按照销售量升序排列
result = this.hits - o.hits;
if (0 == result) {// 如果销售量相同 按照名字降序排列
result = -this.title.compareTo(o.title);
}
}
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("商品名称").append(this.title);
sb.append("销售量").append(this.hits);
sb.append("生产时间").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(this.pubTime)).append("\n");
return sb.toString();
}
}
  • 测试,比较
1
2
3
4
5
6
7
8
9
10
11
12
13
// 时间降序, 销售量升序, 标题降序
public static void main(String[] args) {
List<ComparableExample> item = new ArrayList<ComparableExample>();
item.add(new ComparableExample("abcitems", 30, new Date(System.currentTimeMillis() - 1000 * 60 * 60)));
item.add(new ComparableExample("abcfgitems", 30, new Date(System.currentTimeMillis() - 1000 * 60 * 50)));
item.add(new ComparableExample("abcditems", 100, new Date()));
item.add(new ComparableExample("abefNews", 50, new Date(System.currentTimeMillis() - 1000 * 60 * 60)));
System.out.println("----------排序前----------");
System.out.println(item);
System.out.println("----------排序后----------");
Collections.sort(item);
System.out.println(item);
}
  • 运行结果
1
2
3
4
5
6
7
8
9
10
11
12
----------排序前----------
[商品名称abcitems销售量30生产时间2016-09-20 15:04:37
, 商品名称abcfgitems销售量30生产时间2016-09-20 15:14:37
, 商品名称abcditems销售量100生产时间2016-09-20 16:04:37
, 商品名称abefNews销售量50生产时间2016-09-20 15:04:37
]
----------排序后----------
[商品名称abcditems销售量100生产时间2016-09-20 16:04:37
, 商品名称abcfgitems销售量30生产时间2016-09-20 15:14:37
, 商品名称abcitems销售量30生产时间2016-09-20 15:04:37
, 商品名称abefNews销售量50生产时间2016-09-20 15:04:37
]

2. Comparator 比较器接口

如果需要控制某个类的次序,而该类本身不支持排序(即没有实现Comparable接口);那么可以建立一个该类的比较器来排序,这个比较器只需要实现Comparator接口即可。 换句话说, 通过实现Comparator类来新建一个比较器,然后通过该比较器来对类进行排序。Comparator 接口其实就是一种策略模式的实践

接口作用

  1. 如果一个类已经开放完成,但是在此类建立的初期并没有实现 Comparable 接口,此时肯定是无法进行对象排序操作的,所以为了解决这一的问题,java 又定义了另一个比较器的操作接口 Comparator 此接口定义在 java.util 包中
  2. 为了使用不同的排序标准做准备,比如升序,降序或者其他什么序列

接口仅仅包括两个函数:

1
2
3
4
5
public interface Comparator<T> {
int compare(T o1, T o2);
boolean equals(Object obj);
}
  1. 若一个类要实现Comparator接口:它一定要实现compare(T o1, T o2)函数,但是可以不实现equals函数。 因为任何类,默认都是已经实现了 equals(Object obj) 。 Java 中的一切类都是继承于 java.lang.Object,在 Object.java 中实现了 equals(Object obj) 函数;所以,其它所有的类也相当于都实现了该函数。
  2. int compare(T o1, T o2) 是 “比较 o1 和 o2 的大小”。返回 “负数”,意味着 “o1 比 o2 小”;返回 “零”,意味着 “o1 等于 o2”;返回 “正数”,意味着 “o1 大于 o2”。

比如 商品,我需要按照价格的降序排列,代码如下:

  • 商品类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package com.zsr.test.compare;
public class Product {
private String title;
private int price;
public Product() {
}
public Product(String title, int price) {
super();
this.title = title;
this.price = price;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
@Override
public String toString() {
return "title=" + title + ",price=" + price + "\n";
}
}
  • 定义比较规则:
1
2
3
4
5
6
7
8
9
10
11
package com.zsr.test.compare;
import java.util.Comparator;
public class ProductCompare implements Comparator<Product> {
@Override
public int compare(Product o1, Product o2) {
return -(o1.getPrice() - o2.getPrice() > 0 ? 1 : (o1.getPrice() == o2.getPrice() ? 0 : -1));
}
}
  • 测试, 比较
1
2
3
4
5
6
7
8
9
public static void main(String[] args) {
List<Product> product = new ArrayList<Product>();
product.add(new Product("a", 120));
product.add(new Product("b", 143432));
product.add(new Product("c", 1892));
product.add(new Product("d", 11092));
Collections.sort(product, new ProductCompare());
System.out.println(product);
}
  • 运行结果
1
2
3
4
5
[title=b,price=143432
, title=d,price=11092
, title=c,price=1892
, title=a,price=120
]

3. Comparable 和 Comparator比较

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

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

热评文章