Comparable與Comparator

Prerequisites:

傳回值

Comparable, Comparator都是比較介面,傳回值固定0、正數、負數。

0代表等於。

Comparable介面

Comparable是一個介面,有一個compareTo()方法,功用是比較大小。

Comparable介面原始檔

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

使用方法

泛型介面的文章中,有提到實作泛型介面時要指定泛型類型,以下程式碼指定的泛型類型為MyDate,Intellij實作介面時會把MyDate的類型替代原本的T。

1
2
3
4
5
6
class MyDate implements Comparable<MyDate> {
  @Override
  public int compareTo(MyDate o) {
    return 0;
  }
}

compareTo(MyDate)參數

每一個方法都有隱藏參數this,this就是自己本身物件,而MyDate參數,是外部傳來要比較的物件,不是自己本身。

比較

比較的方法內容自己寫。

比較方法:

  • 自己(this)比參數大,傳正數。
  • 自己(this)比參數小,傳負數。
  • 二者相等傳0。

以下是使用相減作為比較結果,注意,如果是用o.year - this.year,這樣結果就不會符合比較方法。

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
class MyDate implements Comparable<MyDate> {
  private int year;
  private int mon;
  private int date;

  public MyDate(int year, int mon, int date) {
    this.year = year;
    this.mon = mon;
    this.date = date;
  }

  @Override
  public int compareTo(MyDate 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_date = this.date - o.date;
    if (diff_date != 0) {
      return diff_date;
    }
    return 0;
  }
}

測試

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

JDK8 String compareTo

String 實作Comparable<String>。

String字串是用final char[]陣列儲存,注意!是final,代表不能更改為其它字串陣列。

1
2
3
4
public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    private final char value[];
}

compareTo()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public int compareTo(String anotherString) {
    // value是本身的字串陣列
    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;
    // 2個字串在最小長度下,比較內容
    while (k < lim) {
        char c1 = v1[k];
        char c2 = v2[k];
        if (c1 != c2) {
            return c1 - c2;
        }
        k++;
    }
    // 不同長度回傳 長度相減
    return len1 - len2;
}

情況1

value = abcdefg
another = abdd

結果為c(99) - d(100) = -1

情況2

以下二個字串前4個字元都一樣,只有長度不同。

value = abcdefg
another = abcd

傳回3

情況3

以下二個字串前4個字元都一樣,只有長度不同。

value = abcde
another = abcdfg

傳回-3

Comparator

Comparator可以透過匿名類別Lambda,建立物件,只要實作比較的內容,就可單獨使用。

MyDate2提供getYear()、getMon()、getDate(),供其它類別使用。

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 date;

  public MyDate2(int year, int mon, int date) {
    this.year = year;
    this.mon = mon;
    this.date = date;
  }

  public int getYear() {
    return year;
  }

  public int getMon() {
    return mon;
  }

  public int getDate() {
    return date;
  }
}

建立比較器物件,進行比較,compare方法跟Comparable邏輯一模一樣。

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
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) {
        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_date = o1.getDate() - o2.getDate();
        if (diff_date != 0) {
          return diff_date;
        }
        return 0;
      }
    };
    int result = comparator.compare(myDate2, another);
    System.out.println(result);
  }
}
1

Arrays.sort()排序

sort()原始碼

第2個參數是把Comparator傳入,就可以自訂排序的方法。

1
2
3
4
5
6
7
8
9
10
public static <T> void sort(T[] a, Comparator<? super T> c) {
  if (c == null) {
    sort(a);
  } else {
    if (LegacyMergeSort.userRequested)
        legacyMergeSort(a, c);
    else
        TimSort.sort(a, 0, a.length, c, null, 0, 0);
  }
}

Arrays.sort()排序與Comparator

MyDate2增加toString()。

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
class MyDate2 {
  private int year;
  private int mon;
  private int date;

  public int getYear() {
    return year;
  }

  public int getMon() {
    return mon;
  }

  public int getDate() {
    return date;
  }

  public MyDate2(int year, int mon, int date) {
    this.year = year;
    this.mon = mon;
    this.date = date;
  }

  @Override
  public String toString() {
    return "\nMyDate2{" +
        "year=" + year +
        ", mon=" + mon +
        ", date=" + date +
        '}';
  }
}

排序使用自訂Comparator。

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
public class Test {
  public static void main(String[] args) {
    // 透過匿名類別建立物件
    Comparator<MyDate2> comparator = new Comparator<MyDate2>() {
      @Override
      public int compare(MyDate2 o1, MyDate2 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_date = o1.getDate() - o2.getDate();
        if (diff_date != 0) {
          return diff_date;
        }
        return 0;
      }
    };

    // 建立Array,裡面的內容是沒按照年月日順序
    MyDate2[] arr = {
        new MyDate2(2000, 11, 10),
        new MyDate2(2000, 1, 1),
        new MyDate2(2000, 3, 1)
    };
    // 陣列排序,把自訂Comparator傳入。
    Arrays.sort(arr, comparator);
    System.out.println(Arrays.toString(arr));
  }
}
[
MyDate2{year=2000, mon=1, date=1}, 
MyDate2{year=2000, mon=3, date=1}, 
MyDate2{year=2000, mon=11, date=10}]

結果由小到大排列。

由大到小

想把排序結果由大到小排列,只要把o1與o2位置顛倒。

o2.getYear() - o1.getYear()
o2.getMon() - o1.getMon()
o2.getDate() - o1.getDate()
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
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) {
        int diff_y = o2.getYear() - o1.getYear();
        // 不相等代表比較出結果,直接傳回比較結果
        // 之後的程式就沒必要再比較。
        if (diff_y != 0) {
          return diff_y;
        }

        int diff_m = o2.getMon() - o1.getMon();
        if (diff_m != 0) {
          return diff_m;
        }

        int diff_date = o2.getDate() - o1.getDate();
        if (diff_date != 0) {
          return diff_date;
        }
        return 0;
      }
    };
    MyDate2[] arr = {
        new MyDate2(2000, 11, 10),
        new MyDate2(2000, 1, 1),
        new MyDate2(2000, 3, 1)
    };
    Arrays.sort(arr, comparator);
    System.out.println(Arrays.toString(arr));
  }
}
[
MyDate2{year=2000, mon=11, date=10}, 
MyDate2{year=2000, mon=3, date=1}, 
MyDate2{year=2000, mon=1, date=1}]

結果由大到小排列。

results matching ""

    No results matching ""