Comparable與Comparator
Prerequisites:
由小到大 由大到小
假設有陣列如下:
1, 2, 3, 4, 5
由小到大
由小到大是指「目前位置」的值與「下一個位置」的值進行比較,比較方式是「目前位置」的值減「下一個位置」的值。
1, 2, 3, 4, 5
↑ ↑
目 下
前 一
位 個
置 位
置
1 - 2 = -1
傳回結果為-1,代表「目前位置」的值比「下一個位置」的值小,以氣泡排序來說,就不用交換。
if (比較結果 > 0) {
交換
}
假設陣列的值變成以下這樣。
2, 1, 3, 4, 5
↑ ↑
目 下
前 一
位 個
置 位
置
2 - 1 = 1
傳回結果為1,代表「目前位置」的值比「下一個位置」的值大,以氣泡排序來說,就要交換。
「目前位置」,如同下面程式碼的j,「下一個位置」,如同下面程式碼的j + 1,以下程式碼是「目前位置」的值 > 「下一個位置」的值,就交換。
1
2
3
if (arr[j] > arr[j + 1]) {
// 交換
}
「目前位置」的值減「下一個位置」的值 > 0,等同於arr[j] > arr[j + 1]
,二者都是「目前位置」的值「大於」「下一個位置」的值,相減才會是正整數。
由大到小
比較方式是「下一個位置」的值減「目前位置」的值。
1, 2, 3, 4, 5
↑ ↑
目 下
前 一
位 個
置 位
置
「下一個位置」的值減「目前位置」的值
2 - 1 = 1
傳回結果為1,代表「下一個位置」的值比「目前位置」的值大,以氣泡排序來說,比較結果 > 0要交換。
if (比較結果 > 0) {
交換
}
交換之後變成下面這樣,2與1就完成由大到小排序。
2, 1, 3, 4, 5
氣泡排序若要改成由大到小,以下的程式碼「大於>」要變「小於<」,代表「下一個位置j + 1 」的值比「目前位置 j 」的值大,就交換,由大到小,大的數字要在前面。
1
2
3
if (arr[j] < arr[j + 1]) {
// 交換
}
Comparable介面
Comparable是一個介面,有一個compareTo()方法,功用是比較大小。
Comparable介面原始檔
1
2
3
public interface Comparable<T> {
public int compareTo(T o);
}
compareTo(o)參數
每一個方法都有隱藏參數this,this就是「目前位置」物件,而o參數,是「下一個位置」的物件。
1, 2, 3, 4, 5
↑ ↑
目 下
前 一
位 個
置 位
置
比較
由小到大,this - o
由大到小,o - this
this是目前位置物件,o是下一個位置物件。
以下是由小到大。
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
class MyDate implements Comparable<MyDate> {
private int year;
private int mon;
private int day;
public MyDate(int year, int mon, int day) {
this.year = year;
this.mon = mon;
this.day = day;
}
@Override
public int compareTo(MyDate o) {
// 由小到大 this - o
int diff_y = this.year - o.year;
// 不相等代表比較出結果,直接傳回比較結果
// 之後的程式就沒必要再比較。
if (diff_y != 0) {
return diff_y;
}
int diff_m = this.mon - o.mon;
if (diff_m != 0) {
return diff_m;
}
int diff_day = this.day - o.day;
if (diff_day != 0) {
return diff_day;
}
return 0;
}
}
比較日期大小
正數代表myDate > anotherDate
負數代表myDate < anotherDate
0代表myDate == anotherDate
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
public class Test {
public static void main(String[] args) {
MyDate myDate = new MyDate(2000, 11, 11);
MyDate anotherDate = new MyDate(2000, 11, 5);
int res = myDate.compareTo(anotherDate);
System.out.println(res);
System.out.println("==========");
myDate = new MyDate(2000, 11, 1);
anotherDate = new MyDate(2000, 11, 10);
res = myDate.compareTo(anotherDate);
System.out.println(res);
System.out.println("==========");
myDate = new MyDate(2000, 11, 11);
anotherDate = new MyDate(2000, 11, 11);
res = myDate.compareTo(anotherDate);
System.out.println(res);
}
}
6
==========
-9
==========
0
其它:泛型與Comparable
在泛型介面的文章中,有提到實作泛型介面時要指定泛型類型,以下程式碼指定的泛型類型為MyDate,Intellij實作介面時會把MyDate的類型替代原本的T。
1
2
3
4
5
6
class MyDate implements Comparable<MyDate> {
@Override
public int compareTo(MyDate o) {
return 0;
}
}
Comparator
Comparator可以透過匿名類別、Lambda,建立物件,只要實作比較的內容,就可單獨使用。
MyDate2提供getYear()、getMon()、getDay(),供其它類別使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class MyDate2 {
private int year;
private int mon;
private int day;
public MyDate2(int year, int mon, int day) {
this.year = year;
this.mon = mon;
this.day = day;
}
public int getYear() {
return year;
}
public int getMon() {
return mon;
}
public int getDay() {
return day;
}
}
Comparator.compare(o1,o2)
o1是「目前位置」物件,o2是「下一個位置」物件。
由小到大,o1 - o2
由大到小,o2 - o1
1, 2, 3, 4, 5
↑ ↑
目 下
前 一
位 個
置 位
置
比較日期大小
正數代表myDate2 > another
負數代表myDate2 < another
0代表myDate2 == another
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
public class Test {
public static void main(String[] args) {
MyDate2 myDate2 = new MyDate2(2000, 11, 11);
MyDate2 another = new MyDate2(2000, 11, 10);
// 透過匿名類別建立物件
Comparator<MyDate2> comparator = new Comparator<MyDate2>() {
@Override
public int compare(MyDate2 o1, MyDate2 o2) {
// 由小到大,o1 - o2
int diff_y = o1.getYear() - o2.getYear();
if (diff_y != 0) {
return diff_y;
}
int diff_m = o1.getMon() - o2.getMon();
if (diff_m != 0) {
return diff_m;
}
int diff_day = o1.getDay() - o2.getDay();
if (diff_day != 0) {
return diff_day;
}
return 0;
}
};
int result = comparator.compare(myDate2, another);
System.out.println(result);
}
}
1