JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序

前言:暑期应该开始了,因为小区对面的小学这两天早上都没有像以往那样一到七八点钟就人声喧闹、车水马龙。

前两篇文章介绍了Collection框架的主要接口和常用类,例如List、Set、Queue,和ArrayList、HashSet、LinkedList等等。根据核心框架图,相信我们都已经对Collection这个JavaSE中最常用API之一有一个较为全面的认识。

这个学习过程,还可以推及到其他常用开源框架和公司项目的学习和熟悉上面。借助开发工具或说明文档,先是对项目整体有一个宏观的认识,再根据这个认识逐一熟悉各个核心模块。(如果对关于Collection的两篇文章感兴趣的话,可以在文章的最末尾点击链接阅读。)

1.5 遍历对象的Iterator

Collection,顾名思义,就是收集,在JavaSE当中起到了收集对象的作用。

收集完对象之后,有一个非常普遍的需求,就是要遍历所收集的对象。学生报名之后,老师有浏览都有哪些学生已经注册的需要;客户下了订单之后, 商家有看看哪些商品被购买的需要;病人挂号之后,医院工作人员有了解门诊工作量的需要。

1.5.1 遍历对象遇到的问题

如果要写一个forEach()方法,可以显示List收集的所有对象,也许你会这么写:

1     private static void forEach(List list) {
2         int size = list.size();
3         for(int i = 0; i < size; i++) {
4             System.out.println(list.get(i));
5         }
6     }

这个方法适用于所有实现List接口的对象,如ArrayList、LinkedList等。如果要让你写个forEach()方法显示Set收集的所有对象,你该怎么写呢?在查看过Set的API说明文档后,发现有个toArray()方法,可以将Set收集的对象转为Object[]返回,所以你会这么写:

1     private static void forEach(Set set) {
2         for(Object o : set.toArray()) {
3             System.out.println(o);
4         }
5     }

这个方法适用于所有操作Set接口的对象,如HashSet、TreeSet等。如果现在要让你再写一个forEach()方法,可以显示Queue收集的对象,也许你会这么写:

1     private static void forEach(Queue queue) {
2         while(queue.peek() != null) {
3             System.out.println(queue.poll());
4         }
5     }

表面上看来好像是正确的,不过Queue的poll()方法会取出对象,当你显示完Queue中所有对象,Queue也空了。这并不是我们想要的结果,怎么办呢?

1.5.2 使用Iterator

事实上,无论是List、Set还是Queue,都会有个Queue,都会有个iterator()方法,这个方法在JDK1.4之前,是定义在Collection接口中,而它们都继承自Collection,所以也都拥有iterator()的行为。

iterator()方法会返回java.util.Iterator接口的操作对象,这个对象包括了Collection收集的所有对象,你可以使用Iterator的hasNext()看看有无下一个对象,若有的话,再使用next()取得下一个对象。因此,无论List、Set、Queue还是任何Collection,都可以使用以下的forEach()方法来显示所收集的对象:

1     private static void forEach(Collection collection) {
2         Iterator iterator = collection.iterator();
3         while(iterator.hasNext()) {
4             System.out.println(iterator.next());
5         }
6     }

在JDK5之后,原先定义在Collection中的iterator()方法,提升至新的java.util.Iterable父接口,因此在JDK5之后,可以使用以下forEach()方法显示收集的所有对象:

1     private static void forEach(Iterable iterable) {
2         Iterator iterator = iterable.iterator();
3         while(iterator.hasNext()) {
4             System.out.println(iterator.next());
5         }
6     }

接下来,我们可以写一个比较完整的demo,看看是否能正确使用forEach()方法:

 1 import java.util.*;
 2
 3 /**
 4  * Iterator实验用例
 5  */
 6 public class ForEach {
 7     private static void forEach(Iterable iterable) {
 8         Iterator iterator = iterable.iterator();
 9         while(iterator.hasNext()) {
10             System.out.println(iterator.next());
11         }
12     }
13
14     public static void main(String[] args) {
15         List list = Arrays.asList("Tim", "Jack", "Jane");
16         forEach(list);
17         forEach(new HashSet(list));
18         forEach(new ArrayDeque(list));
19     }
20 }

运行之后,我们可以得到以下结果:

1.5.3 Iterator小结

遍历显示所收集的所有对象,这是使用Celletion框架时频繁会遇到的需求。在了解List、Set、Queue如何显示收集对象之后,我们意外地发现可以通过使用定义在上层接口的iterator()方法,达到正确、安全地满足这一需求的目的。

无论是学习Colletion等JavaSE的API框架,还是熟悉开源或公司项目,在熟悉各个模块的同时,也要有抽象的理解能力来宏观地理解项目。在满足一些普遍常用需求时,可以找到更通用、复用程度更高的解决方案。如果能做到这一点,就不会犯新手常犯的“只见树木不见森林”的错误。

1.6 收集对象后的排序

在收集对象之后,对对象进行排序是常用的动作。

我的上家是做ERP系统的,常常有这样的需求:几家供应商提供的报价单价格不一样,系统用户希望可以在按照报价从低到高查看商品的报价;生产物料紧缺时,他们希望根据物料需求日期从近到远查看;同一个生产件,不同的生产配方和工艺流程产生的生产成本是不一样的,他们希望能按照成本从低到高查看生产明细。以上的这些需求,都要求系统对所收集的不同对象进行排序

1.6.1 Collection自带的排序算法

java.util.Collections提供有sort()方法,用来满足对对象进行排序的需求。由于必须有索引才能进行排序,因此Collections的sort()方法接受List实现对象。例如以下这段demo:

 1 import java.util.*;
 2
 3 /**
 4  * Collections的sort()方法实验用例
 5  */
 6 public class Sort {
 7     public static void main(String[] args) {
 8         List numbers = Arrays.asList(10, 3, 4, 21, 9);
 9         Collections.sort(numbers);
10         System.out.println(numbers);
11     }
12 }

执行结果我们可以看到已经排好序的一串数字:

可是,如果我们需要排序的对象稍微复杂一点点,会出现什么样的情况呢?

 1 import java.util.*;
 2
 3 /**
 4  * Collections的sort()方法实验用例2
 5  */
 6 class Account {
 7     private String name;
 8     private int balance;
 9
10     Account (String name,  int balance) {
11         this.name = name;
12         this.balance = balance;
13     }
14
15     @Override
16     public String toString() {
17         return String.format("Account(%s, %d)", name, balance);
18     }
19 }
20
21 public class Sort {
22     public static void main(String[] args) {
23         List accounts = Arrays.asList(
24                 new Account("Tim", 100),
25                 new Account("Tom", 1300),
26                 new Account("Jack", 5)
27         );
28         Collections.sort(accounts);
29         System.out.println(accounts);
30     }
31 }

运行结果出现了抛出ClassCastException报错,到底是怎么回事呢?

1.6.2 实现Comparable

要说原因,是因为你根本没告诉Collections的sort()方法,到底要根据Account的name还是balance进行排序。用一句时下流行的话说:“我有什么办法,我也很绝望啊。”

Collections的sort()方法要求被排序的对象必须实现java.lang.Comparable接口,这个接口有个compareTo()方法必须返回大于0、等于0或小于0的数。这有什么用呢?我们直接来看下面这个针对账户余额排序的demo就了解了:

 1 import java.util.*;
 2
 3 /**
 4  * Collections的sort()方法实验用例3
 5  */
 6 class Account implements Comparable{
 7     private String name;
 8     private int balance;
 9
10     Account (String name,  int balance) {
11         this.name = name;
12         this.balance = balance;
13     }
14
15     @Override
16     public String toString() {
17         return String.format("Account(%s, %d)", name, balance);
18     }
19
20     @Override
21     public int compareTo(Object o) {
22         Account other = (Account) o;
23         return this.balance - other.balance;
24     }
25 }
26
27 public class Sort {
28     public static void main(String[] args) {
29         List accounts = Arrays.asList(
30                 new Account("Tim", 100),
31                 new Account("Tom", 1300),
32                 new Account("Jack", 5)
33         );
34         Collections.sort(accounts);
35         System.out.println(accounts);
36     }
37 }

Collections的sort()方法在取得a对象与b对象进行比较时,会先将对象Cast为Comparable(也因为这样,如果对象没实现这个接口,就会抛出ClassCastException),然后调用a.compareTo(b),如果a对象顺序上小于b对象,必须返回小于0的值;如果顺序上相等则返回0;如果顺序上a大于b,则要返回大于0的值。因此,上面的demo输出结果会是按照余额从小到大排列:

1.6.3 实现Comparator

如果你有用过Collections对所收集的String对象排序,你应该会知道JavaSE是按照A、B、C的字母表来排序的。可是,如果今天突然有个需求,要让排序结果反过来呢?

首先,String已经实现了Comparable接口,我们很难进行修改。另外,由于String声明为final,我们也没有办法通过继承的方式重新定义compareTo()方法。不过幸好,JavaSE给这种情况留下了备用的解决方案。

Collections的sort()方法有另一个重载版本,可以接受java.util.Comparator接口的操作对象。如果使用这个版本,排序方式将根据Comparator的compare()方法的定义来决定。例如下面这个demo:

 1 import java.util.*;
 2
 3 /**
 4  * Collections的sort()方法实验用例4
 5  */
 6 class StringComparator implements Comparator {
 7     @Override
 8     public int compare(Object o1, Object o2) {
 9         String str1 = (String) o1;
10         String str2 = (String) o2;
11         return -str1.compareTo(str2);
12     }
13 }
14
15 public class Sort {
16     public static void main(String[] args) {
17         List words = Arrays.asList("B", "C", "A", "X", "Z", "Y");
18         Collections.sort(words, new StringComparator());
19         System.out.println(words);
20     }
21 }

结果如下,符合我们之前的期望:

1.6.4 Sort()小结

在Java的规范中,与顺序有关的行为,通常要不就是对象本身是Comparable,即实现了Comparable接口,要不就是另行制定Comparator对象告知如何排序。

这就是深入学习API的好处,可以介绍遇到麻烦的次数。另外,无论你的工作语言是Java还是PHP、C#,熟悉语言规范是必不可少的内功,是衡量一个程序员实力的硬指标。

相关文章推荐:

JavaSE中Collection集合框架学习笔记(1)——具有索引的List

JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue

如果你喜欢我的文章,可以扫描关注我的个人公众号“李文业的思考笔记”。

不定期地会推送我的原创思考文章。

时间: 2024-10-02 09:52:29

JavaSE中Collection集合框架学习笔记(3)——遍历对象的Iterator和收集对象后的排序的相关文章

JavaSE中Collection集合框架学习笔记(2)——拒绝重复内容的Set和支持队列操作的Queue

前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的文章,大多都是以罗列记忆点的形式书写的,没有谈论实现细节和逻辑原理.作为个人笔记无可厚非,但是并不利于他人学习.希望能通过这种比较“费劲”的讲解,帮助我自己.也帮助读者们更好地学习Java.掌握Java. 无论你跟我一样需要应聘,还是说在校学生学习Java基础,都对入门和进一步启发学习有所帮助.(关

JavaSE中Collection集合框架学习笔记(1)——具有索引的List

前言:因为最近要重新找工作,Collection(集合)是面试中出现频率非常高的基础考察点,所以好好恶补了一番. 复习过程中深感之前的学习不系统,而且不能再像刚毕业那样死背面试题,例如:String是固定长度的,StringBuffer和StringBuilder的长度是可以变化的.如果一旦问得深入一点,问为什么有这样的区别就傻眼了,只能一脸呆萌地看着面试官. 因此想要通过写文章的形式,系统地总结学习的内容,例如Collection架构是怎样的.有哪些相关的继承和接口实现,这样才能了解什么时候应

Java集合框架学习笔记之集合与Collection API

一.CollectionAPI 集合是一系列对象的聚集(Collection).集合在程序设计中是一种重要的数据接口.Java中提供了有关集合的类库称为CollectionAPI. 集合实际上是用一个对象代表一组对象,在集合中的每个对象称为一个元素.在集合中的各个元素的具体类型可以不同,但一般说来,它们都是由相同的类派生出来的(而这一点并不难做到,因为Java中的所有类都是Object的子类).在从集合中检索出各个元素是,常常要根据其具体类型不同而进行相应的强制类型转换. Collection

Java集合框架学习笔记

本文为学习笔记,学习课程为慕课网Java入门第三季中的集合框架部分,若需要研究文中的代码,可前往下载.http://www.imooc.com/learn/110 1. List(Collection子接口) 1.1 实例化 List list = new ArrayList(); ??List是一个接口,不可直接实例化,通常情况下ArrayList实现类进行实例化. 1.2 增 1.2.1 add(obj) ??直接将obj对象加入List末位. 1.2.2 add(i, obj) ??将ob

集合框架学习笔记

集合框架由来 Java2之前,Java是没有完整的集合框架的.它只有一些简单的可以自扩容的容器类,比如 Vector,Stack,Hashtable等 为什么存在容器(可以存储多个数据) 数组的弊端: 1,长度是不可变的,一旦数组初始化之后,长度是固定的 2,在N个地方需要存储多个数据,都得专门写数组的操作方法,代码和功能重复 3,数组定义方法参差不齐集合框架:是为表示和操作集合而规定的一种统一的标准的体系结构.任何集合框架都包含三大块内容:对外的接口,接口的实现和对集合运算的算法为什么需要集合

hibernate框架学习笔记4:主键生成策略、对象状态

创建一个实体类: package domain; public class Customer { private Long cust_id; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_linkman; private String cust_phone; private Stri

JavaSE中线程与并行API框架学习笔记——线程为什么会不安全?

前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事.当然,在学校里考试之前当然要把所有内容学一遍和复习一遍.但是,到了社会里做事,很多时候都是边做边学.应聘如此,工作如此,很多的挑战都是如此.没办法,硬着头皮上吧. 3.5 线程的分组管理 在实际的开发过程当中,可能会有多个线程同时存在,这对批量处理有了需求.这就有点像用迅雷下载电视剧,假设你在同时

JavaSE中线程与并行API框架学习笔记1——线程是什么?

前言:虽然工作了三年,但是几乎没有使用到多线程之类的内容.这其实是工作与学习的矛盾.我们在公司上班,很多时候都只是在处理业务代码,很少接触底层技术. 可是你不可能一辈子都写业务代码,而且跳槽之后新单位很可能有更高的技术要求.除了干巴巴地翻书,我们可以通过两个方式来解决这个问题:一是做业余项目,例如在github上传自己的demo,可以实际使用:二是把自己的学习心得写成博客,跟同行们互相交流. 3.1 线程的初窥门径 我们在之前的文章里提到的程序其实都是单线程程序,也就说启动的程序从main()程

Yii框架学习笔记(二)将html前端模板整合到框架中

选择Yii 2.0版本框架的7个理由 http://blog.chedushi.com/archives/8988 刚接触Yii谈一下对Yii框架的看法和感受 http://bbs.csdn.net/topics/390807796 更多内容 百度:yii 前端 http://my.oschina.net/u/1472492/blog/221085 摘要 Yii框架学习笔记(二)将html前端模板整合到框架中 原文地址:http://www.ldsun.com/1309.html 上一节成功将Y