List中的数据如何根据对象的某一个或多个字段排序引出Comparable和comparator的使用

引入题目:List中的数据如何根据对象的某一个或多个字段排序?

 

第一节  对于引入题目的探讨

首先把引入题目表述的清楚一些,在一个List中存储的是一些对象实例,而对象实例包含多个属性字段,我们要根据对象的某个或者多个属性来对List进行排序。

假设List中存储的都是Student类的实例,Student类包含name、gender、id属性。首先根据Student实例中的name属性排序,如果两个名字相同,就再根据id属性排序。

Student类源码如下:

  1. <strong>package chapter1;  
  2.    
  3. public class Student {  
  4.     private String name;  
  5.     private String gender;  
  6.     private int id;  
  7.      
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.      
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.      
  16.     public String getGender() {  
  17.         return gender;  
  18.     }  
  19.      
  20.     public void setGender(String gender) {  
  21.         this.gender = gender;  
  22.     }  
  23.      
  24.     public int getId() {  
  25.         return id;  
  26.     }  
  27.      
  28.     public void setId(int id) {  
  29.         this.id = id;  
  30.     }  
  31. }</strong>  
          假设有三个学生:


                                     姓名       性别        ID

                                     宋超       男          100120

                                     伍星       男          100121

                                     宋超       女          100122

            把这三个学生存储在List里面,要求首先按照姓名进行排序,如果姓名相同就按照ID排序。

 

1.1.1第一种实现方式:使用Comparable接口;

          使用这种方式时,Student类必须继承这个接口,并且实现compareTo方法。并且compareTo方法是这个接口的唯一方法。需要注意到一点,在《Effective Java》第十二条中,提供了一个通用的写法,也就是在类继承的Comparable接口的时候,利用泛型指明能比较的类型。把Student类改写如下:

               

[java] view plaincopy
  1. package chapter1;  
  2.    
  3. public classStudent implementsComparable<Student>{  
  4.     private String name;  
  5.     private String gender;  
  6.     private int id;  
  7.      
  8.     public String getName() {  
  9.         return name;  
  10.     }  
  11.      
  12.     public void setName(String name) {  
  13.         this.name = name;  
  14.     }  
  15.      
  16.     public String getGender() {  
  17.         return gender;  
  18.     }  
  19.      
  20.     public void setGender(Stringgender) {  
  21.         this.gender = gender;  
  22.     }  
  23.      
  24.     public int getId() {  
  25.         return id;  
  26.     }  
  27.      
  28.     public void setId(int id) {  
  29.         this.id = id;  
  30.     }  
  31.    
  32.     @Override  
  33.     public int compareTo(Student arg0){  
  34.         //String、Integer、Double、Float等类型都实现有compareTo方法  
  35.         if(this.name.compareTo(arg0.name) == 0) {  
  36.             return Integer.valueOf(id).compareTo(Integer.valueOf(arg0.id));  
  37.         }else{  
  38.             return this.name.compareTo(arg0.name);  
  39.         }  
  40.     }  
  41. }  


 

在《Effective Java》中对于compareTo方法有以下几点提示:

·自反性

·传递性

·对称性

·最好和equals方法值相同

 

那么,在客户端调用的时候,直接写如下代码:


[java] view plaincopy
  1. package chapter1;  
  2.    
  3. import java.util.ArrayList;  
  4. import java.util.Collections;  
  5. import java.util.List;  
  6.    
  7. public class ClientInterface {  
  8.                    publicstatic void main(String[] args) {  
  9.                              
  10.                             Studentsongchao = new Student();  
  11.                             songchao.setGender("Man");  
  12.                             songchao.setId(100150);  
  13.                             songchao.setName("SongChao");  
  14.                              
  15.                             Studentwuxing = new Student();  
  16.                             wuxing.setGender("Man");  
  17.                             wuxing.setId(100121);  
  18.                             wuxing.setName("WuXing");  
  19.                              
  20.                             Studentsongchao2 = new Student();  
  21.                             songchao2.setGender("Women");  
  22.                             songchao2.setId(100130);  
  23.                             songchao2.setName("SongChao");  
  24.                              
  25.                             List<Student>students = new ArrayList<Student>();  
  26.                             students.add(songchao);  
  27.                             students.add(wuxing);  
  28.                             students.add(songchao2);  
  29.                             for(Studentstudent : students) {  
  30.                                      System.out.println("Name  " + student.getName() + " ID  " + student.getId());  
  31.                             }  
  32.                              
  33.                             System.out.println();  
  34.                              
  35.                             Collections.sort(students);  
  36.                             for(Studentstudent : students) {  
  37.                                      System.out.println("Name  " + student.getName() + " ID  " + student.getId());  
  38.                             }  
  39.                    }  
  40.                     
  41. }  



 

输出结果如下:


[java] view plaincopy
  1. Name  SongChao ID  100150  
  2. Name  WuXing ID  100121  
  3. Name  SongChao ID  100130  
  4.    
  5. Name  SongChao ID  100130  
  6. Name  SongChao ID  100150  
  7. Name  WuXing ID 100121  



 

1.1.2另外一种方式:直接使用比较器comparator

 

         直接使用比较器的情况下,Student类不必继承Comparable接口,当然也不必实现compareTo方法。

         直接看调用代码,但是注意一点,这里的Student类没有继承Comparable接口。

         直接在上面的客户端代码中,把Collections.sort方法替换为:


[java] view plaincopy
  1. Collections.sort(students, newComparator<Student>() {  
  2.    
  3.             @Override  
  4.             public int compare(Student arg0,Student arg1) {  
  5.                 if(arg0.getName().compareTo(arg1.getName())== 0) {  
  6.                     return Integer.valueOf(arg0.getId()).compareTo(Integer.valueOf(arg1.getId()));  
  7.                 } else {  
  8.                     returnarg0.getName().compareTo(arg1.getName());  
  9.                 }  
  10.             }  
  11.              
  12.         });  



 

输出结果和第一种方式相同。

还有一个注意点,那就是排序的顺序,是按照由小到大还是由大到小。上面那种方式显然是从小到大排序,那么如何从大到小排序?只要改变参数顺序即可:

   

[java] view plaincopy
  1. Collections.sort(students,newComparator<Student>() {  
  2.   
  3.            @Override  
  4.            public int compare(Student arg0,Student arg1) {  
  5.                if(arg1.getName().compareTo(arg0.getName()) == 0) {  
  6.                    return Integer.valueOf(arg1.getId()).compareTo(Integer.valueOf(arg0.getId()));  
  7.                }else{  
  8.                    return arg1.getName().compareTo(arg0.getName());  
  9.                }  
  10.            }  
  11.             
  12.        });  


 

以上是对于sort方法的小总结,Arrays中的sort方法类似。

 

第二节  Collections与Arrays概念分析

1.2.1 Collection和Collections的区别

     Collection是java.util下的接口,是各种集合结构的父接口。继承它的接口主要有Set和List。

     Collections是java.util下的专用静态类,包含有各种有关集合操作的静态方法。提供一系列静态方法实现对各种集合的搜索、排序、线程安全化等操作。

 

1.2.2 Array与Arrays的区别

     Arrays也是一个静态类,专门用来操作array,提供搜索、排序等静态方法,还提供一个允许把数组当作列表查看的静态工厂方法asList。

 

 

第三节  其它常用方法总结(第四节为使用中容易出现错误的方法介绍,第五节源代码)

1.3.1 Collections中的常用方法

            java.util.Collections提供了一些静态方法实现了基于list容器的一些常用算法:

1)  addAll(Collection c, T… elements);//将所有指定的元素添加到指定的collection中。

2)  void sort(List);//对list容器里的元素进行排序 

3)  void reverse(List);//对List容器对象进行逆序排序 

4)  void copy(Listdest,List src)//将List src容器里的内容全部拷贝到List dest容器中 

5)   int binarySearch(List,Object)//对于顺序的List容器中采用折半查找的方法查找特定的对象

6)   boolean disjoint(Collection c1, Collection c2);//如果两个指定的collection中没有相同的元素,返回true。

7)   fill(List list, T obj); // 使用指定元素替换指定列表中的所有元素。

8)   int frequency(Collection c, Object o);//返回指定的Collection中对于指定对象的元素数。

9)   indexOfSubList(List src, List target);//返回源表中第一次出现指定目标列表的起始位置,如果没有这样的列表就返回-1。

10) lastIndexOfSubList(List src, List target);//最后一次的起始位置,没有则返回-1

11) max(Collection coll);//根据元素的自然顺序,返回collection的最大值;

12) max(Collection coll,Comparator comp);//根据比较器产生的顺序,返回最大元素。

13) min同上

14) replaceAll(List list, T oldVal, T newVal);//使用另外一个值替换列表中出现的所有某一指定值。

15) reverseOrder();//逆转comparable接口的对象collection的自然顺序。例如:假设a是一个字符串数组,那么:

Arrays.sort(a, Collections.reverseOrder());将会按照字典逆序排序。

16) reverseOrder(Comparator cmp);返回一个强行逆转比较器的顺序

17) rotate(List list, intdistance);//根据指定的距离轮换列表中的元素。

18) shuffle(List list);//对列表随机排序

19) shuffle(List list, Random rnd);//根据指定的随机源排序

20) swap(List list, int i, int j);//在指定列表的指定位置处交换元素


 

[java] view plaincopy
  1. package chapter1;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Arrays;  
  5. import java.util.Collection;  
  6. import java.util.Collections;  
  7. import java.util.List;  
  8.   
  9. public class CollectionsMethod {  
  10.   
  11.     /** 
  12.      * @param args 
  13.      */  
  14.     public static void main(String[] args) {  
  15.         List<Integer> collection = new ArrayList<Integer>();  
  16.           
  17.         /*1----两种方式 */  
  18.         Collections.addAll(collection, 123);  
  19.         System.out.println(collection.toString());  
  20.           
  21.         Integer[] moreInts = {10749};  
  22.         Collections.addAll(collection, moreInts);  
  23.         System.out.println(collection.toString());  
  24.           
  25.         /*2----简单类型下sort的使用 */  
  26.         Collections.sort(collection);  
  27.         System.out.println(collection.toString());  
  28.           
  29.         /*3----逆序*/   
  30.         Collections.reverse(collection);  
  31.         System.out.println(collection.toString());  
  32.           
  33.         /*4----复制*/   
  34.         List<Integer> copyList = new  ArrayList(Arrays.asList( new  Object[collection.size()]));  
  35.         Collections.copy(copyList, collection);  
  36.         copyList.remove(0);  
  37.         System.out.println(copyList.size());  
  38.         System.out.println(copyList.toString());  
  39.         System.out.println(collection.toString());  
  40.           
  41.         /*4----另外一种方式 */   
  42.         List array = new ArrayList(collection);  
  43.         System.out.println(array.toString());  
  44.         System.out.println(collection.toString());  
  45.           
  46.         /*5----二分查找,首先把列表排序才行 */  
  47.         List<Integer> bsList = new ArrayList<Integer>();  
  48.         bsList.add(9);  
  49.         bsList.add(12);  
  50.         bsList.add(2);  
  51.         bsList.add(78);  
  52.         bsList.add(10);  
  53.         System.out.println(bsList.toString());        
  54.         Collections.sort(bsList);  
  55.         System.out.println(bsList);  
  56.         int index = Collections.binarySearch(bsList, 20);  
  57.         System.out.println(index);  
  58.           
  59.         /*6----判定两个collection中是否没有相同的元素,返回布尔值*/  
  60.         System.out.println(Collections.disjoint(bsList, array));  
  61.           
  62.         /*7----返回指定对象的个数*/  
  63.         System.out.println(Collections.frequency(bsList, 10));  
  64.           
  65.         /*8----子列表在指定列表中的位置*/  
  66.         List<Integer> subList = new ArrayList<Integer>();  
  67.         subList.add(9);  
  68.         subList.add(10);  
  69.         subList.add(5);  
  70.         subList.add(1);  
  71.         System.out.println(Collections.indexOfSubList(bsList, subList));  
  72.           
  73.         /*9----返回最大最小值 & 倒序 & 循环移位 & 交换*/  
  74.         System.out.println(Collections.max(subList));  
  75.         Collections.reverseOrder();  
  76.         System.out.println(subList.toString());  
  77.         Collections.rotate(subList, 2);  
  78.         System.out.println(subList.toString());  
  79.         Collections.swap(subList, 02);  
  80.         System.out.println(subList.toString());  
  81.           
  82.           
  83.     }  
  84.   
  85. }  


1.3.2 Arrays中的常用方法

Arrays中的方法比较简单,基本上分为

1) asList方法,把一个数组转换成list

2) 二分查找方法,可以指定在数组中的范围内执行

3) toString方法

4) sort方法

 

第四节  容易出现错误的方法介绍

1.4.1Collections中容易出现错误的方法介绍

    1)copy方法

所以使用了Collections.copy()方法来进行拷贝,但是这样就接触到了此方法所报出的异常: 
举例如下:
List src1 = new  ArrayList( 3 )
src1.add( " a " );
src2.add( " b " );
src3.add( " c " );

如果你使用下面方法copy链表
/** **************************** */ 
List des1 = new  ArrayList( 3 );
Collections.copy(des1,src1);
/** **************************** */ 
将会出错,抛出数组越界异常。明明已经设置了长度为3,为什么还会出错?
打印出des1.size()才知道des1的长度为0;3表示的是这个List的容纳能力为3,并不是说des1中就有了3个元素。查看api才知 道,它的capacity(容纳能力大小)可以指定(最好指定)。而初始化时size的大小永远默认为0,只有在进行add和remove等相关操作 时,size的大小才变化。然而进行copy()时候,首先做的是将desc1的size和src1的size大小进行比较,只有当desc1的 size 大于或者等于src1的size时才进行拷贝,否则抛出IndexOutOfBoundsException异常。

原文地址:https://www.cnblogs.com/allenzhaox/p/3201820.html