引入题目:List中的数据如何根据对象的某一个或多个字段排序?
第一节 对于引入题目的探讨
首先把引入题目表述的清楚一些,在一个List中存储的是一些对象实例,而对象实例包含多个属性字段,我们要根据对象的某个或者多个属性来对List进行排序。
假设List中存储的都是Student类的实例,Student类包含name、gender、id属性。首先根据Student实例中的name属性排序,如果两个名字相同,就再根据id属性排序。
Student类源码如下:
假设有三个学生:
- package chapter1;
- public class Student {
- private String name;
- private String gender;
- private int id;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getGender() {
- return gender;
- }
- public void setGender(String gender) {
- this.gender = gender;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- }
姓名 性别 ID
宋超 男 100120
伍星 男 100121
宋超 女 100122
把这三个学生存储在List里面,要求首先按照姓名进行排序,如果姓名相同就按照ID排序。
1.1.1第一种实现方式:使用Comparable接口;
使用这种方式时,Student类必须继承这个接口,并且实现compareTo方法。并且compareTo方法是这个接口的唯一方法。需要注意到一点,在《Effective Java》第十二条中,提供了一个通用的写法,也就是在类继承的Comparable接口的时候,利用泛型指明能比较的类型。把Student类改写如下:
- package chapter1;
- public classStudent implementsComparable
{ - private String name;
- private String gender;
- private int id;
- public String getName() {
- return name;
- }
- public void setName(String name) {
- this.name = name;
- }
- public String getGender() {
- return gender;
- }
- public void setGender(Stringgender) {
- this.gender = gender;
- }
- public int getId() {
- return id;
- }
- public void setId(int id) {
- this.id = id;
- }
- @Override
- public int compareTo(Student arg0){
- //String、Integer、Double、Float等类型都实现有compareTo方法
- if(this.name.compareTo(arg0.name) == 0) {
- return Integer.valueOf(id).compareTo(Integer.valueOf(arg0.id));
- }else{
- return this.name.compareTo(arg0.name);
- }
- }
- }
在《Effective Java》中对于compareTo方法有以下几点提示:
·自反性
·传递性
·对称性
·最好和equals方法值相同
那么,在客户端调用的时候,直接写如下代码:
- package chapter1;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- public class ClientInterface {
- publicstatic void main(String[] args) {
- Studentsongchao = new Student();
- songchao.setGender("Man");
- songchao.setId(100150);
- songchao.setName("SongChao");
- Studentwuxing = new Student();
- wuxing.setGender("Man");
- wuxing.setId(100121);
- wuxing.setName("WuXing");
- Studentsongchao2 = new Student();
- songchao2.setGender("Women");
- songchao2.setId(100130);
- songchao2.setName("SongChao");
- List
students = new ArrayList (); - students.add(songchao);
- students.add(wuxing);
- students.add(songchao2);
- for(Studentstudent : students) {
- System.out.println("Name " + student.getName() + " ID " + student.getId());
- }
- System.out.println();
- Collections.sort(students);
- for(Studentstudent : students) {
- System.out.println("Name " + student.getName() + " ID " + student.getId());
- }
- }
- }
输出结果如下:
- Name SongChao ID 100150
- Name WuXing ID 100121
- Name SongChao ID 100130
- Name SongChao ID 100130
- Name SongChao ID 100150
- Name WuXing ID 100121
1.1.2另外一种方式:直接使用比较器comparator
直接使用比较器的情况下,Student类不必继承Comparable接口,当然也不必实现compareTo方法。
直接看调用代码,但是注意一点,这里的Student类没有继承Comparable接口。
直接在上面的客户端代码中,把Collections.sort方法替换为:
- Collections.sort(students, newComparator
() { - @Override
- public int compare(Student arg0,Student arg1) {
- if(arg0.getName().compareTo(arg1.getName())== 0) {
- return Integer.valueOf(arg0.getId()).compareTo(Integer.valueOf(arg1.getId()));
- } else {
- returnarg0.getName().compareTo(arg1.getName());
- }
- }
- });
输出结果和第一种方式相同。
还有一个注意点,那就是排序的顺序,是按照由小到大还是由大到小。上面那种方式显然是从小到大排序,那么如何从大到小排序?只要改变参数顺序即可:
- Collections.sort(students,newComparator
() { - @Override
- public int compare(Student arg0,Student arg1) {
- if(arg1.getName().compareTo(arg0.getName()) == 0) {
- return Integer.valueOf(arg1.getId()).compareTo(Integer.valueOf(arg0.getId()));
- }else{
- return arg1.getName().compareTo(arg0.getName());
- }
- }
- });
以上是对于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);//在指定列表的指定位置处交换元素
- package chapter1;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collection;
- import java.util.Collections;
- import java.util.List;
- public class CollectionsMethod {
- /**
- * @param args
- */
- public static void main(String[] args) {
- List
collection = new ArrayList (); - /*1----两种方式 */
- Collections.addAll(collection, 1, 2, 3);
- System.out.println(collection.toString());
- Integer[] moreInts = { 10, 7, 4, 9};
- Collections.addAll(collection, moreInts);
- System.out.println(collection.toString());
- /*2----简单类型下sort的使用 */
- Collections.sort(collection);
- System.out.println(collection.toString());
- /*3----逆序*/
- Collections.reverse(collection);
- System.out.println(collection.toString());
- /*4----复制*/
- List
copyList = new ArrayList(Arrays.asList( new Object[collection.size()])); - Collections.copy(copyList, collection);
- copyList.remove(0);
- System.out.println(copyList.size());
- System.out.println(copyList.toString());
- System.out.println(collection.toString());
- /*4----另外一种方式 */
- List array = new ArrayList(collection);
- System.out.println(array.toString());
- System.out.println(collection.toString());
- /*5----二分查找,首先把列表排序才行 */
- List
bsList = new ArrayList (); - bsList.add(9);
- bsList.add(12);
- bsList.add(2);
- bsList.add(78);
- bsList.add(10);
- System.out.println(bsList.toString());
- Collections.sort(bsList);
- System.out.println(bsList);
- int index = Collections.binarySearch(bsList, 20);
- System.out.println(index);
- /*6----判定两个collection中是否没有相同的元素,返回布尔值*/
- System.out.println(Collections.disjoint(bsList, array));
- /*7----返回指定对象的个数*/
- System.out.println(Collections.frequency(bsList, 10));
- /*8----子列表在指定列表中的位置*/
- List
subList = new ArrayList (); - subList.add(9);
- subList.add(10);
- subList.add(5);
- subList.add(1);
- System.out.println(Collections.indexOfSubList(bsList, subList));
- /*9----返回最大最小值 & 倒序 & 循环移位 & 交换*/
- System.out.println(Collections.max(subList));
- Collections.reverseOrder();
- System.out.println(subList.toString());
- Collections.rotate(subList, 2);
- System.out.println(subList.toString());
- Collections.swap(subList, 0, 2);
- System.out.println(subList.toString());
- }
- }
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异常。