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

jopen 8年前發布 | 18K 次閱讀 設計模式 JDK Java開發

排序類:

 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 }

測試類:

 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 }

運行一下看看結果:

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接口試一下:

 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 }

看一下結果:

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怎么寫:

 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接口。

Strategy設計模式之Comparable&Comparator接口 完!

參見:

給jdk寫注釋系列之jdk1.6容器(7)-TreeMap源碼解析

給jdk寫注釋系列之jdk1.6容器(8)-TreeSet&NavigableMap&NavigableSet源碼解析

來自: http://www.cnblogs.com/tstd/p/5090401.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!