給jdk寫註釋系列之jdk1.6容器(9)-Strategy設計模式之Comparable&Comparator接口

  前面咱們說TreeMap和TreeSet都是有順序的集合,而順序的維持是要靠一個比較器Comparator或者map的key實現Comparable接口。
     既然說到排序,首先咱們不用去關心什麼是Strategy設計模式,也不用關心它爲了解決什麼問題而存在,咱們直接從排序開始看。
 
1.排序
 
     假設咱們有一個int數組須要排序,想想應該怎麼實現,固然首先要有一個int數組,而後呢,而後須要有一個能夠實現排序的方法或類,怎麼實現排序呢,說到排序算法可能不少人都會什麼快速、冒泡、插入。。。咱們這裏不是講排序算法,隨便選一種來用就行了,網上一直流傳會冒泡能夠直接入職xx公司,固然是一句腹黑的玩笑話了,那麼咱們就用冒泡嘍。
  來試一下:
  排序類:
 1 public class DataSort {
 2 
 3         public static void sort( int[] arr) {
 4                for (int i = arr.length; i > 0; i--) {
 5                       for (int j = 0; j < i - 1; j++) {
 6                             // 若是前一個比後一個大,那麼就把大值交換到後面去
 7                             if (arr[j] > arr[j + 1]) {
 8                                    int temp = arr[j];
 9                                   arr[j] = arr[j + 1];
10                                   arr[j + 1] = temp;
11                            }
12                      }
13               }
14        }
15 }

  測試類:   html

 1 public class Test {
 2 
 3         public static void main(String[] args) {
 4                int[] arr = new int[] { 9, 5, 2, 7 };
 5               DataSort. sort(arr);
 6                for (int i : arr) {
 7                      System. out.print(i + " " );
 8               }
 9        }
10 }

  運行一下看看結果:算法

2 5 7 9
  
  ok,咱們已經完成排序了,可是,我不只要去對int進行排序,還要對其餘的事物進行排序,好比說人,那怎麼作呢?
     首先咱們須要先定義一個Penson類,有什麼屬性呢,簡單一點就有姓名,年齡和收入,定義一下:
 1 public class Person {
 2 
 3         private String name ;
 4         private int age;
 5         private int money;
 6 
 7         public Person(String name, int age, int money) {
 8                this.name = name;
 9                this.age = age;
10                this.money = money;
11        }
12 
13         public String getName() {
14                return name ;
15        }
16 
17         public void setName(String name) {
18                this.name = name;
19        }
20 
21         public int getAge() {
22                return age ;
23        }
24 
25         public void setAge(int age) {
26                this.age = age;
27        }
28 
29         public int getMoney() {
30                return money ;
31        }
32 
33         public void setMoney(int money) {
34                this.money = money;
35        }
36 
37         @Override
38         public String toString() {
39                return "Person [name=" + name + ", age=" + age + ", money=" + money
40                            + "]";
41        }
42 
43 }
44  

  Penson這個類定義完成了,怎麼進行排序呢,好比你說誰收入高誰老大,ok那麼咱們就按收入寫一下排序方法:設計模式

 1 public class DataSort {
 2 
 3         public static void sort( int[] arr) {
 4                for (int i = arr.length; i > 0; i--) {
 5                       for (int j = 0; j < i - 1; j++) {
 6                             // 若是前一個比後一個大,那麼就把大值交換到後面去
 7                             if (arr[j] > arr[j + 1]) {
 8                                    int temp = arr[j];
 9                                   arr[j] = arr[j + 1];
10                                   arr[j + 1] = temp;
11                            }
12                      }
13               }
14        }
15        
16         public static void sort(Person[] arr) {
17                for (int i = arr.length; i > 0; i--) {
18                       for (int j = 0; j < i - 1; j++) {
19                             // 若是前一個比後一個大,那麼就把大值交換到後面去
20                             if (arr[j].getMoney() > arr[j + 1].getMoney()) {
21                                   Person temp = arr[j];
22                                   arr[j] = arr[j + 1];
23                                   arr[j + 1] = temp;
24                            }
25                      }
26               }
27        }
28 }

  咱們在DataSort中重寫了一個sort(Person[] arr)方法,用來給Person類進行排序,測試一下吧:數組

 1 public class Test {
 2 
 3         public static void main(String[] args) {
 4                // int[] arr = new int[] { 9, 5, 2, 7 };
 5                // DataSort.sort(arr);
 6                // for (int i : arr) {
 7                // System.out.print(i + " ");
 8                // }
 9 
10               Person p1 = new Person("張三" , 25, 100); // 張三,25歲,年薪100w
11               Person p2 = new Person("李四" , 30, 10); // 李四,30歲,年薪10w
12               Person p3 = new Person("王五" , 20, 1000); // 王五,25歲,年薪1000w
13               Person[] arr = new Person[] { p1, p2, p3 };
14 
15               DataSort. sort(arr);
16                for (Person p : arr) {
17                      System. out.println(p + " " );
18               }
19        }
20 }

  看下結果:框架

Person [name=李四, age=30, money=10]
Person [name=張三, age=25, money=100]
Person [name=王五, age=20, money=1000]
  結果正確對不對,是否是感受本身so牛x,我寫的排序類,既能夠排序整數int,又能夠排序自定義的Person類,是否是有點飄飄然了。
     等等,這有一盆冷水,我還要求能夠對阿貓阿狗進行排序,你說再重寫一個sort方法唄,那我還要求對電腦手機進行排序,對花花草草進行排序。。。如今是否是很苦惱,你必定在想,我要寫一種萬能的排序方法能夠對任何東西進行排序。這個時候你沒有瘋而是進入設計的大門了,此時什麼多態、封裝,繼承等等概念撲面而來,惋惜的是你仍是寫不出萬能的排序方法。能不能換一種思路,咱們來提供一個標準,一個方法論,只提供排序的算法,具體的怎麼比較大小你本身看着辦,這麼作能夠嗎?來試一下:
 
2.排序的方法論
 
     2.1 comparable
 
     咱們先明確下目標,咱們要實現的任然是排序,可是咱們不去進行大小比較,比較大小的功能由具體的類本身負責,這麼一想好像就清晰了許多的樣子。
     首先咱們定義一個接口,提供一個標準給要進行排序的類:
     
1 public interface MyComparable {
2 
3         /**
4         * 返回值大於0說明當前比較的Object大,小於0說明被比較的Object大,
5         * 等於0說明兩個Object相等
6         */
7         public int compareTo(Object o);
8 }
  MyComparable接口咱們寫好了,咱們規定,只要排序就必須實現MyComparable接口,並且要重寫compareTo方法,返回一個int值來告訴我誰大誰小。
     DataSort的排序方法sort怎麼作呢,很簡單了:
 1 public class DataSort {
 2 
 3         public static void sort(MyComparable[] arr) {
 4                for (int i = arr.length; i > 0; i--) {
 5                       for (int j = 0; j < i - 1; j++) {
 6                             if (arr[j].compareTo(arr[j + 1]) > 0) {
 7                                   MyComparable temp = arr[j];
 8                                   arr[j] = arr[j + 1];
 9                                   arr[j + 1] = temp;
10                            }
11                      }
12               }
13        }
14        
15 }

  是否是很簡單了,只要用compareTo的返回結果就能夠了,下面咱們讓Person實現MyComparable接口試一下:ide

 1 public class Person implements MyComparable {
 2 
 3         private String name ;
 4         private int age;
 5         private int money;
 6 
 7         public Person(String name, int age, int money) {
 8                this.name = name;
 9                this.age = age;
10                this.money = money;
11        }
12 
13         public String getName() {
14                return name ;
15        }
16 
17         public void setName(String name) {
18                this.name = name;
19        }
20 
21         public int getAge() {
22                return age ;
23        }
24 
25         public void setAge(int age) {
26                this.age = age;
27        }
28 
29         public int getMoney() {
30                return money ;
31        }
32 
33         public void setMoney(int money) {
34                this.money = money;
35        }
36 
37         @Override
38         public String toString() {
39                return "Person [name=" + name + ", age=" + age + ", money=" + money
40                            + "]";
41        }
42 
43         @Override
44         public int compareTo(Object o) {
45               Person p = (Person)o;
46                if (this.money > p. money) {
47                       return 1;
48               } else {
49                       return -1;
50               }
51        }
52 
53 }

  測試一下:測試

 1 public class Test {
 2 
 3         public static void main(String[] args) {
 4                // int[] arr = new int[] { 9, 5, 2, 7 };
 5                // DataSort.sort(arr);
 6                // for (int i : arr) {
 7                // System.out.print(i + " ");
 8                // }
 9 
10               Person p1 = new Person("張三" , 25, 100); // 張三,25歲,年薪100w
11               Person p2 = new Person("李四" , 30, 10); // 李四,30歲,年薪10w
12               Person p3 = new Person("王五" , 20, 1000); // 王五,25歲,年薪1000w
13               Person[] arr = new Person[] { p1, p2, p3 };
14 
15               DataSort. sort(arr);
16                for (Person p : arr) {
17                      System. out.println(p + " " );
18               }
19        }
20 }

  看一下結果:this

Person [name=李四, age=30, money=10]
Person [name=張三, age=25, money=100]
Person [name=王五, age=20, money=1000]
  和預期的同樣對不對,也就是說明咱們的排序沒有問題,如今你又開始飄飄然了,我寫的排序終於完美了,能夠對任何類進行排序,什麼阿貓阿狗你只要實現MyComparable接口,通通來吧,哈哈哈。
     等等,這裏還有一盆冷水,我讓你對長整型Long進行排序,,Long沒問題啊、只要實現。。。實現什麼,是否是傻了,Long是已經存在的類,你不可能從新編譯它讓它實現你的MyComparable接口吧,哎,這可怎麼辦。。。
     等等先別哭,我還有另外一盆冷水,對於Person類個人想法變了,不想用收入做爲比較了,我想按照年齡進行比較,也沒準我某天想按照年齡+收入的組合進行比較,反正我就是這麼任性,反正我就是讓你如今猜不透。你的須要一天三變,我不能把代碼該來該去吧,這樣的話開發急了會和產品打架的,怎麼辦呀,這兩個問題我一個不會弄。。。
 
      2.2 comparator
 
     那麼問題來了,想一下,能不能進一步的封裝,既然我不能去改變一些類的代碼,那麼我能不能將比較大小的邏輯拿出來呢?既然你的須要老是變,而我又預測不到,那麼我能不能把你的需求也進行抽象,你得需求細節你本身實現,我提供給你邏輯框架呢?答案是確定的,說幹就幹!
     咱們要將比較大小的邏輯拿出來,首先仍是要定義一個標準,要使用我進行排序,就得安裝規矩來。
1 public interface MyComparator {
2         public int compare(Object o1, Object o2);
3 }

  注意,這個接口不是讓你的排序類來實現的,看看我sort怎麼寫:spa

 1 public class DataSort {
 2 
 3         public static void sort(MyComparable[] arr) {
 4                for (int i = arr.length; i > 0; i--) {
 5                       for (int j = 0; j < i - 1; j++) {
 6                             if (arr[j].compareTo(arr[j + 1]) > 0) {
 7                                   MyComparable temp = arr[j];
 8                                   arr[j] = arr[j + 1];
 9                                   arr[j + 1] = temp;
10                            }
11                      }
12               }
13        }
14        
15         public static void sort(Object[] arr, MyComparator c) {
16                for (int i = arr.length; i > 0; i--) {
17                       for (int j = 0; j < i - 1; j++) {
18                             if (c.compare(arr[j], arr[j + 1]) > 0) {
19                                   Object temp = arr[j];
20                                   arr[j] = arr[j + 1];
21                                   arr[j + 1] = temp;
22                            }
23                      }
24               }
25        }
26        
27 }
  我又重寫了一個sort方法,你只要把你的比較大小邏輯提供給我,我就能給你排序了。來試一下:
     首先我寫一個具體的比較大小邏輯類:
 1 public class PersonAgeComparator implements MyComparator {
 2 
 3         @Override
 4         public int compare(Object o1, Object o2) {
 5               Person p1 = (Person) o1;
 6               Person p2 = (Person) o2;
 7               
 8                if (p1.getAge() - p2.getAge() > 0) {
 9                       return 1;
10               } else {
11                       return -1;
12               }
13        }
14 
15 }

  具體看看怎麼來用:設計

 1 public class Test {
 2 
 3         public static void main(String[] args) {
 4 //            int[] arr = new int[] { 9, 5, 2, 7 };
 5 //            DataSort.sort(arr);
 6 //            for (int i : arr) {
 7 //                   System.out.print(i + " ");
 8 //            }
 9 
10               Person p1 = new Person("張三" , 25, 100); // 張三,25歲,年薪100w
11               Person p2 = new Person("李四" , 30, 10); // 李四,30歲,年薪10w
12               Person p3 = new Person("王五" , 20, 1000); // 王五,25歲,年薪1000w
13               Person[] arr = new Person[] { p1, p2, p3 };
14 
15               DataSort. sort(arr, new PersonAgeComparator());
16                for (Person p : arr) {
17                      System. out.println(p + " " );
18               }
19        }
20 }

  我只須要把個人比較大小邏輯類傳入sort就能夠了,看下結果:

Person [name=王五, age=20, money=1000] 
Person [name=張三, age=25, money=100] 
Person [name=李四, age=30, money=10] 
  哇,成功了,如今你在告訴我,要比較Long類型,ok啊,寫一個LongComparator就好了,,還要組合Person類的年齡和收入,那我寫一個PersonAgeAndMoneyComparator就行了,這下完美了,我已經作到了足夠靈活,任意的擴展,哈哈哈。。。
     彆着急,我還有問題(我弄死你),此次不是冷水了,放心。想一個問題,如今Person類和PersonAgeComparator類兩個是獨立的,它們是靠sort這個排序方法聯繫在一塊兒的。可是我想讓他們兩個聯繫密切一些,咱們在講低耦合的時候也在講高內聚,畢竟Person類和他的比較大小邏輯是緊密聯繫的,怎麼辦呢,那就是將Comparator封裝成Person的一個屬性。
     來看一下:
 1 public class Person implements MyComparable {
 2 
 3         private String name ;
 4         private int age;
 5         private int money;
 6        
 7         private MyComparator comparator = new PersonAgeComparator();
 8 
 9         public Person(String name, int age, int money) {
10                this.name = name;
11                this.age = age;
12                this.money = money;
13        }
14 
15         public Person(String name, int age, int money, MyComparator comparator) {
16                this.name = name;
17                this.age = age;
18                this.money = money;
19                this.comparator = comparator;
20        }
21 
22         public String getName() {
23                return name ;
24        }
25 
26         public void setName(String name) {
27                this.name = name;
28        }
29 
30         public int getAge() {
31                return age ;
32        }
33 
34         public void setAge(int age) {
35                this.age = age;
36        }
37 
38         public int getMoney() {
39                return money ;
40        }
41 
42         public void setMoney(int money) {
43                this.money = money;
44        }
45 
46         @Override
47         public String toString() {
48                return "Person [name=" + name + ", age=" + age + ", money=" + money
49                            + "]";
50        }
51 
52         @Override
53         public int compareTo(Object o) {
54                return comparator .compare(this, o);
55        }
56 
57 }
  咱們將MyComparator接口封裝成了Person的一個屬性,具體要用什麼樣的比較大小邏輯,你調用方傳給我,固然你不傳的話,我本身也有一個默認的策略,這樣我就不怕你忘記了。
 
     講到這裏Comparable和Comparator就講完了,可是好像有個概念咱們尚未說,那就是什麼是Strategy設計模式 。
 
3.Strategy設計模式
 
     Strategy設計模式中文叫作策略設計模式,其實咱們就算不知道什麼是策略模式不是也將上面的問題搞定了麼,因此啊,不要太在乎於概念的東西,首先你要會用,能解決。
 
     不過仍是得來解釋下策略模式的概念,大致說,不標準:策略模式是針對一組算法,將每一個算法封裝到具備共同接口的獨立的類中,使得他們能夠互相的替換,而客戶端在調用的時候可以互不影響。
     策略模式一般有這麼幾個角色:
     (1) 環境(Context)角色:持有一個Strategy的引用。——Person類
     (2) 抽象策略(Strategy)角色:這是一個抽象角色,一般由一個接口或抽象類實現。此角色給出全部的具體策略類所需的接口。——MyComparator接口
     (3) 具體策略(ConcreteStrategy)角色:包裝了相關的算法或行爲。——PersonAgeComparator類
 
     策略模式的優缺點是什麼:
     優勢:(1)將具體算法邏輯與客戶類分離,(2)避免了大量的if else判斷
     缺點:(1)每一個算法一個類,產生了太多的類,(2)客戶端要知道全部的策略類,以便決定使用哪個。
 
     想一想怎麼樣能有嘗試解決策略模式的缺點。。。那就是工廠模式。ok這裏不是主要講設計模式,就到這裏了。
 
4.回憶TreeMap的比較大小
 1 public V put(K key, V value) {
 2         ......
 3         ......
 4 
 5         // split comparator and comparable paths
 6         // 當前使用的比較器
 7         Comparator<? super K> cpr = comparator ;
 8         // 若是比較器不爲空,就是用指定的比較器來維護TreeMap的元素順序
 9         if (cpr != null) {
10              // do while循環,查找key要插入的位置(也就是新節點的父節點是誰)
11             do {
12                 // 記錄上次循環的節點t
13                 parent = t;
14                 // 比較當前節點的key和新插入的key的大小
15                 cmp = cpr.compare(key, t. key);
16                  // 新插入的key小的話,則以當前節點的左孩子節點爲新的比較節點
17                 if (cmp < 0)
18                     t = t. left;
19                 // 新插入的key大的話,則以當前節點的右孩子節點爲新的比較節點
20                 else if (cmp > 0)
21                     t = t. right;
22                 else
23               // 若是當前節點的key和新插入的key想的的話,則覆蓋map的value,返回
24                     return t.setValue(value);
25             // 只有當t爲null,也就是沒有要比較節點的時候,表明已經找到新節點要插入的位置
26             } while (t != null);
27         }
28         else {
29             // 若是比較器爲空,則使用key做爲比較器進行比較
30             // 這裏要求key不能爲空,而且必須實現Comparable接口
31             if (key == null)
32                 throw new NullPointerException();
33             Comparable<? super K> k = (Comparable<? super K>) key;
34             // 和上面同樣,喜歡查找新節點要插入的位置
35             do {
36                 parent = t;
37                 cmp = k.compareTo(t. key);
38                 if (cmp < 0)
39                     t = t. left;
40                 else if (cmp > 0)
41                     t = t. right;
42                 else
43                     return t.setValue(value);
44             } while (t != null);
45         }
46         
47         ......
48         ......
49     }
  如今理解TreeMap爲何要判斷有沒有Comparator了吧。。若是沒有的話,就用key去比較大小,可是要求key實現Comparable接口。
 
  來看一下jdk中Comparator和Comparable是怎麼定義的吧。
1 public interface Comparator<T> {
2     int compare(T o1, T o2);
3     boolean equals(Object obj);
4 }

 

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

  惟一不一樣的是Comparator接口中要求重寫equals方法,用於比較是否相等。

 
 
Strategy設計模式之Comparable&Comparator接口 完!
 
 
 
參見:
相關文章
相關標籤/搜索