ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

集合容器中Strategy设计模式之Comparable&Comparator接口

2021-11-13 22:32:41  阅读:190  来源: 互联网

标签:Comparable name Comparator int money arr Person 设计模式 public


文章目录

1 集合容器中Strategy设计模式

前面我们说TreeMapTreeSet都是有顺序的集合,而顺序的维持是要靠一个比较器Comparator或者mapkey实现Comparable接口
既然说到排序,首先我们不用去关心什么是Strategy设计模式,也不用关心它为了解决什么问题而存在,我们直接从排序开始看。

1.1 排序

假设有一个int数组需要排序,首先要有一个int数组,然后需要有一个可以实现排序的方法或类,说到排序算法可能很多人都会什么快速、冒泡、插入。。。这里不是讲排序算法,随便选一种来用就好了,网上一直流传会冒泡可以直接入职xx公司,当然是一句腹黑的玩笑话了,那么我们就用冒泡
来试一下:
  排序类:

  public class DataSort { 
          public static void sort( int[] arr) {
                 for (int i = arr.length; i > 0; i--) {
                        for (int j = 0; j < i - 1; j++) {
        // 如果前一个比后一个大,那么就把大值交换到后面去
                    if (arr[j] > arr[j + 1]) {
                                     int temp = arr[j];
                                    arr[j] = arr[j + 1];
                                   arr[j + 1] = temp;
                            }
                      }
               }
        }
}

测试类:

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

运行一下看看结果:
2 5 7 9

已经完成排序,但是,不仅要去对int进行排序,还要对其他的事物进行排序,比如说人,那怎么做呢?
首先需要先定义一个Penson类,有什么属性呢,简单一点就有姓名,年龄和收入,定义一下:

  public class Person {
  
          private String name ;
          private int age;
          private int money;
  
          public Person(String name, int age, int money) {
                 this.name = name;
                 this.age = age;
                this.money = money;
        }
 
         public String getName() {
                return name ;
        }
 
         public void setName(String name) {
                this.name = name;
        }
 
         public int getAge() {
                return age ;
        }
 
         public void setAge(int age) {
                this.age = age;
        }
 
         public int getMoney() {
                return money ;
        }
 
         public void setMoney(int money) {
                this.money = money;
        }
 
         @Override
         public String toString() {
                return "Person [name=" + name + ", age=" + age + ", money=" + money
                            + "]";
        }
 
 }

Penson这个类定义完成了,怎么进行排序呢,那么我们就按收入写一下排序方法:

  public class DataSort {
  
          public static void sort( int[] arr) {
                 for (int i = arr.length; i > 0; i--) {
                        for (int j = 0; j < i - 1; j++) {
             // 如果前一个比后一个大,那么就把大值交换到后面去 
                         if (arr[j] > arr[j + 1]) {
                                     int temp = arr[j];
                                    arr[j] = arr[j + 1];
                                   arr[j + 1] = temp;
                            }
                      }
               }
        }
        
         public static void sort(Person[] arr) {
                for (int i = arr.length; i > 0; i--) {
                       for (int j = 0; j < i - 1; j++) {
              // 如果前一个比后一个大,那么就把大值交换到后面去
                          if (arr[j].getMoney() > arr[j + 1].getMoney()) {
                                   Person temp = arr[j];
                                   arr[j] = arr[j + 1];
                                   arr[j + 1] = temp;
                            }
                      }
               }
        }
}

DataSort中重写了一个sort(Person[] arr)方法,用来给Person类进行排序,测试一下吧:

  public class Test {
  
          public static void main(String[] args) {
                 // int[] arr = new int[] { 9, 5, 2, 7 };
                 // DataSort.sort(arr);
                 // for (int i : arr) {
                 // System.out.print(i + " ");
                 // }  
           Person p1 = new Person("张三" , 25, 100); // 张三,25岁,年薪100w
            Person p2 = new Person("李四" , 30, 10); // 李四,30岁,年薪10w
            Person p3 = new Person("王五" , 20, 1000); // 王五,25岁,年薪1000w
           Person[] arr = new Person[] { p1, p2, p3 };
               DataSort. sort(arr);
                for (Person p : arr) {
                      System. out.println(p + " " );
               }
        }
}

看下结果:
Person [name=李四, age=30, money=10]
Person [name=张三, age=25, money=100]
Person [name=王五, age=20, money=1000]

虽然可以对Person排序,但是要是对其他对象排序就不行了,最好有个统一通用方法

1.2 排序的方法论

1.2.1 comparable

先明确下目标,我们要实现的仍然是排序,但是我们不去进行大小比较,比较大小的功能由具体的类自己负责
首先我们定义一个接口,提供一个标准给要进行排序的类:

 public interface MyComparable {
 
         /**         * 返回值大于0说明当前比较的Object大,小于0说明被比较的Object大,
         * 等于0说明两个Object相等
         */
                  public int compareTo(Object o);
 }

MyComparable接口我们写好了,我们规定,只要排序就必须实现MyComparable接口,而且要重写compareTo方法,返回一个int值来告诉谁大谁小。
DataSort的排序方法sort怎么做呢,很简单了:

  public class DataSort {
  
          public static void sort(MyComparable[] arr) {
                 for (int i = arr.length; i > 0; i--) {
                        for (int j = 0; j < i - 1; j++) {
                              if (arr[j].compareTo(arr[j + 1]) > 0) {
                                    MyComparable temp = arr[j];
                                    arr[j] = arr[j + 1];
                                    arr[j + 1] = temp;
                            }
                      }
               }
        }
        
}

只要用compareTo的返回结果就可以了,下面我们让Person实现MyComparable接口试一下:

  public class Person implements MyComparable {
  
          private String name ;
          private int age;
          private int money;
 
          public Person(String name, int age, int money) {
                 this.name = name;
                 this.age = age;
                this.money = money;
        }
 
         public String getName() {
                return name ;
        }
 
         public void setName(String name) {
                this.name = name;
        }
 
         public int getAge() {
                return age ;
        }
 
         public void setAge(int age) {
                this.age = age;
        }
 
         public int getMoney() {
                return money ;
        }
 
         public void setMoney(int money) {
                this.money = money;
        }
 
         @Override
         public String toString() {
                return "Person [name=" + name + ", age=" + age + ", money=" + money
                            + "]";
        }
 
         @Override
        public int compareTo(Object o) {
               Person p = (Person)o;
                if (this.money > p. money) {
                       return 1;
               } else {
                       return -1;
               }
       }
}

测试一下:

  public class Test {
  
          public static void main(String[] args) {
                 // int[] arr = new int[] { 9, 5, 2, 7 };
                 // DataSort.sort(arr);
                 // for (int i : arr) {
                 // System.out.print(i + " ");
                 // } 9 
             Person p1 = new Person("张三" , 25, 100); // 张三,25岁,年薪100w
             Person p2 = new Person("李四" , 30, 10); // 李四,30岁,年薪10w
             Person p3 = new Person("王五" , 20, 1000); // 王五,25岁,年薪1000w
             Person[] arr = new Person[] { p1, p2, p3 };

               DataSort. sort(arr);
                for (Person p : arr) {
                      System. out.println(p + " " );
               }
        }
}

看一下结果:
Person [name=李四, age=30, money=10]
Person [name=张三, age=25, money=100]
Person [name=王五, age=20, money=1000]

和预期的一样,也就是说明排序没有问题
但是假如对长整型Long进行排序,不可能重新编译它让它实现你的MyComparable接口吧
假如对于Person类,不想用收入作为比较了,想按照年龄进行比较,或者按照年龄+收入的组合进行比较,目前就不好解决了

1.2.2 comparator

那么问题来了,想一下,能不能进一步的封装,既然不能去改变一些类的代码,那么能不能将比较大小的逻辑拿出来呢?既然需求总是变,那么能不能把需求也进行抽象,需求细节自己实现
我们要将比较大小的逻辑拿出来,首先还是要定义一个标准,要使用进行排序,就得按照规矩来。

 public interface MyComparator {
         public int compare(Object o1, Object o2);
}

注意,这个接口不是让排序类来实现的,看看sort怎么写:

  public class DataSort {
  
          public static void sort(MyComparable[] arr) {
                 for (int i = arr.length; i > 0; i--) {
                        for (int j = 0; j < i - 1; j++) {
                              if (arr[j].compareTo(arr[j + 1]) > 0) {
                                    MyComparable temp = arr[j];
                                    arr[j] = arr[j + 1];
                                    arr[j + 1] = temp;
                            }
                      }
               }
        }
        
         public static void sort(Object[] arr, MyComparator c) {
                for (int i = arr.length; i > 0; i--) {
                       for (int j = 0; j < i - 1; j++) {
                             if (c.compare(arr[j], arr[j + 1]) > 0) {
                                   Object temp = arr[j];
                                   arr[j] = arr[j + 1];
                                   arr[j + 1] = temp;
                            }
                      }
               }
        }
        
}

又重写了一个sort方法,只要把比较大小逻辑提供下,就能给你排序了。来试一下:
首先写一个具体的比较大小逻辑类:

  public class PersonAgeComparator implements MyComparator {
  
          @Override
          public int compare(Object o1, Object o2) {
               Person p1 = (Person) o1;
                Person p2 = (Person) o2;
                
                 if (p1.getAge() - p2.getAge() > 0) {
                        return 1;
               } else {
                       return -1;
               }
        }
}

具体看看怎么来用:

  public class Test {
  
          public static void main(String[] args) {
  //            int[] arr = new int[] { 9, 5, 2, 7 };
  //            DataSort.sort(arr);
  //            for (int i : arr) {
  //                   System.out.print(i + " ");
  //            } 9 
               Person p1 = new Person("张三" , 25, 100); // 张三,25岁,年薪100w
              Person p2 = new Person("李四" , 30, 10); // 李四,30岁,年薪10w
              Person p3 = new Person("王五" , 20, 1000); // 王五,25岁,年薪1000w
              Person[] arr = new Person[] { p1, p2, p3 };
 
               DataSort. sort(arr, new PersonAgeComparator());
                for (Person p : arr) {
                      System. out.println(p + " " );
               }
        }
}

只需要把比较大小逻辑类传入sort就可以了,看下结果:
Person [name=王五, age=20, money=1000] 
Person [name=张三, age=25, money=100] 
Person [name=李四, age=30, money=10] 

假如现在Person类PersonAgeComparator类两个是独立的,它们是靠sort这个排序方法联系在一起的。但是想让他们两个联系密切一些,我们在讲低耦合的时候也在讲高内聚,毕竟Person类和它的比较大小逻辑是紧密联系的,怎么办呢,那就是将Comparator封装成Person的一个属性。
来看一下:

  public class Person implements MyComparable {
  
          private String name ;
          private int age;
          private int money;
         
          private MyComparator comparator = new PersonAgeComparator();
  
          public Person(String name, int age, int money) {
                this.name = name;
                this.age = age;
                this.money = money;
        }
 
         public Person(String name, int age, int money, MyComparator comparator) {
                this.name = name;
                this.age = age;
                this.money = money;
                this.comparator = comparator;
        }
 
         public String getName() {
                return name ;
        }
 
         public void setName(String name) {
                this.name = name;
        }
 
         public int getAge() {
                return age ;
        }
 
         public void setAge(int age) {
                this.age = age;
        }
 
         public int getMoney() {
                return money ;
        }
 
        public void setMoney(int money) {
                this.money = money;
        }
 
         @Override
         public String toString() {
                return "Person [name=" + name + ", age=" + age + ", money=" + money
                            + "]";
        }
 
         @Override
         public int compareTo(Object o) {
                return comparator.compare(this, o);
        }
}

MyComparator接口封装成了Person的一个属性,具体要用什么样的比较大小逻辑,调用方法,当然不传的话,自己也有一个默认的策略,这样就不怕不传了

讲到这里ComparableComparator就讲完了,但是好像有个概念我们还没有说,那就是什么是Strategy设计模式

1.3 Strategy设计模式

Strategy设计模式中文叫做策略设计模式,其实我们就算不知道什么是策略模式不是也将上面的问题搞定了,所以啊,不要太在意于概念的东西,首先你要会用,能解决。
不过还是得来解释下策略模式的概念:策略模式是针对一组算法,将每个算法封装到具有共同接口的独立的类中,使得他们可以互相的替换,而客户端在调用的时候能够互不影响。
策略模式通常有这么几个角色:

  • 环境(Context)角色:持有一个Strategy的引用。——Person类
  • 抽象策略(Strategy)角色:这是一个抽象角色,通常由一个接口或抽象类实现。此角色给出所有的具体策略类所需的接口。——MyComparator接口
  • 具体策略(ConcreteStrategy)角色:包装了相关的算法或行为。——PersonAgeComparator

策略模式的优缺点是什么:
优点:(1)将具体算法逻辑与客户类分离,
(2)避免了大量的if else判断
缺点:(1)每个算法一个类,产生了太多的类,
(2)客户端要知道所有的策略类,以便决定使用哪一个。

1.4 回忆TreeMap的比较大小

  public V put(K key, V value) {
          ......
          ......
  
          // split comparator and comparable paths
          // 当前使用的比较器 
                   Comparator<? super K> cpr = comparator ;
          // 如果比较器不为空,就是用指定的比较器来维护TreeMap的元素顺序 
                   if (cpr != null) {
              // do while循环,查找key要插入的位置(也就是新节点的父节点是谁)
                           do {
                 // 记录上次循环的节点t
                                 parent = t;
                 // 比较当前节点的key和新插入的key的大小
                                  cmp = cpr.compare(key, t. key);
                  // 新插入的key小的话,则以当前节点的左孩子节点为新的比较节点
                                   if (cmp < 0)
                     t = t. left;
                 // 新插入的key大的话,则以当前节点的右孩子节点为新的比较节点
                                  else if (cmp > 0)
                     t = t. right;
                 else
                               // 如果当前节点的key和新插入的key想的的话,则覆盖map的value,返回
                         return t.setValue(value);
             // 只有当t为null,也就是没有要比较节点的时候,代表已经找到新节点要插入的位置
             } while (t != null);
         }
         else {
             // 如果比较器为空,则使用key作为比较器进行比较
             // 这里要求key不能为空,并且必须实现Comparable接口
                         if (key == null)
                 throw new NullPointerException();
             Comparable<? super K> k = (Comparable<? super K>) key;
             // 和上面一样,喜欢查找新节点要插入的位置
                         do {
                 parent = t;
                 cmp = k.compareTo(t. key);
                 if (cmp < 0)
                     t = t. left;
                 else if (cmp > 0)
                     t = t. right;
                 else
                  return t.setValue(value);
             } while (t != null);
         }
         
         ......
         ......
}

现在理解TreeMap为什么要判断有没有Comparator了吧。。如果没有的话,就用key去比较大小,但是要求key实现Comparable接口。

来看一下jdk中ComparatorComparable是怎么定义

 public interface Comparator<T> {
     int compare(T o1, T o2);
     boolean equals(Object obj);
 }

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

唯一不同的是Comparator接口中要求重写equals方法,用于比较是否相等

标签:Comparable,name,Comparator,int,money,arr,Person,设计模式,public
来源: https://blog.csdn.net/u012060033/article/details/121310514

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有