zoukankan      html  css  js  c++  java
  • 给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 }

      测试类:   

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

     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接口。
     
      来看一下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接口 完!
     
     
     
    参见:
     
         
     
  • 相关阅读:
    电脑端口被占用
    listview初始化后仍为空
    java.lang.NoClassDefFoundError
    int型转换成byte型
    Listview列表上显示按钮
    6.手动实现信号于槽的连接过程
    3.22TextEdit设置html以及pushButton暂停与播放实现
    计算器
    QT学习之QMediaPlayer
    03.27随机数产生、Lcd使用,文本框追加、
  • 原文地址:https://www.cnblogs.com/tstd/p/5090401.html
Copyright © 2011-2022 走看看