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

时间:2022-08-25 19:47:26

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

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

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

1.5 遍历对象的Iterator

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

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

1.5.1 遍历对象遇到的问题

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

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

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

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

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

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

表面上看来好像是正确的,不过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()方法来显示所收集的对象:

     private static void forEach(Collection collection) {
Iterator iterator = collection.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}

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

     private static void forEach(Iterable iterable) {
Iterator iterator = iterable.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
}

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

 import java.util.*;

 /**
* Iterator实验用例
*/
public class ForEach {
private static void forEach(Iterable iterable) {
Iterator iterator = iterable.iterator();
while(iterator.hasNext()) {
System.out.println(iterator.next());
}
} public static void main(String[] args) {
List list = Arrays.asList("Tim", "Jack", "Jane");
forEach(list);
forEach(new HashSet(list));
forEach(new ArrayDeque(list));
}
}

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

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

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:

 import java.util.*;

 /**
* Collections的sort()方法实验用例
*/
public class Sort {
public static void main(String[] args) {
List numbers = Arrays.asList(10, 3, 4, 21, 9);
Collections.sort(numbers);
System.out.println(numbers);
}
}

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

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

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

 import java.util.*;

 /**
* Collections的sort()方法实验用例2
*/
class Account {
private String name;
private int balance; Account (String name, int balance) {
this.name = name;
this.balance = balance;
} @Override
public String toString() {
return String.format("Account(%s, %d)", name, balance);
}
} public class Sort {
public static void main(String[] args) {
List accounts = Arrays.asList(
new Account("Tim", 100),
new Account("Tom", 1300),
new Account("Jack", 5)
);
Collections.sort(accounts);
System.out.println(accounts);
}
}

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

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

1.6.2 实现Comparable

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

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

 import java.util.*;

 /**
* Collections的sort()方法实验用例3
*/
class Account implements Comparable{
private String name;
private int balance; Account (String name, int balance) {
this.name = name;
this.balance = balance;
} @Override
public String toString() {
return String.format("Account(%s, %d)", name, balance);
} @Override
public int compareTo(Object o) {
Account other = (Account) o;
return this.balance - other.balance;
}
} public class Sort {
public static void main(String[] args) {
List accounts = Arrays.asList(
new Account("Tim", 100),
new Account("Tom", 1300),
new Account("Jack", 5)
);
Collections.sort(accounts);
System.out.println(accounts);
}
}

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

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

1.6.3 实现Comparator

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

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

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

 import java.util.*;

 /**
* Collections的sort()方法实验用例4
*/
class StringComparator implements Comparator {
@Override
public int compare(Object o1, Object o2) {
String str1 = (String) o1;
String str2 = (String) o2;
return -str1.compareTo(str2);
}
} public class Sort {
public static void main(String[] args) {
List words = Arrays.asList("B", "C", "A", "X", "Z", "Y");
Collections.sort(words, new StringComparator());
System.out.println(words);
}
}

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

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

1.6.4 Sort()小结

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

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

相关文章推荐:

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

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

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

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

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

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

  1. JavaSE中Collection集合框架学习笔记&lpar;2&rpar;——拒绝重复内容的Set和支持队列操作的Queue

    前言:俗话说“金三银四铜五”,不知道我要在这段时间找工作会不会很艰难.不管了,工作三年之后就当给自己放个暑假. 面试当中Collection(集合)是基础重点.我在网上看了几篇讲Collection的 ...

  2. JavaSE中Collection集合框架学习笔记&lpar;1&rpar;——具有索引的List

    前言:因为最近要重新找工作,Collection(集合)是面试中出现频率非常高的基础考察点,所以好好恶补了一番. 复习过程中深感之前的学习不系统,而且不能再像刚毕业那样死背面试题,例如:String是 ...

  3. Java集合框架学习笔记

    集合类的由来:对象用于封装特有数据,对象多了需要存储,如果对象的长度不确定,就使用集合存储. 集合特点1.用于存储对象的容器.2.集合的长度可变.3.集合中不可以存储基本类型 集合容器因为内部的数据结 ...

  4. 集合框架学习笔记&lt&semi;二&gt&semi;

    1.什么是ArrayList ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: 动态的增加和减少元素 实现了ICollection和ILis ...

  5. 集合框架学习笔记&lt&semi;三&gt&semi;

    一些重要的区别 set与list的区别: set是无索引的,list是有索引的: ArrayList与LinkList的区别: 前者是基于数组实现的,后者是基于链表实现的: 两者的使用方法一样,但是在 ...

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

    创建一个实体类: package domain; public class Customer { private Long cust_id; private String cust_name; pri ...

  7. JavaSE中Map框架学习笔记

    前言:最近几天都在生病,退烧之后身体虚弱.头疼.在床上躺了几天,什么事情都干不了.接下来这段时间,要好好加快进度才好. 前面用了三篇文章的篇幅学习了Collection框架的相关内容,而Map框架相对 ...

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

    前言:虽然工作了三年,但是几乎没有使用到多线程之类的内容.这其实是工作与学习的矛盾.我们在公司上班,很多时候都只是在处理业务代码,很少接触底层技术. 可是你不可能一辈子都写业务代码,而且跳槽之后新单位 ...

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

    前言:休整一个多月之后,终于开始投简历了.这段时间休息了一阵子,又病了几天,真正用来复习准备的时间其实并不多.说实话,心里不是非常有底气. 这可能是学生时代遗留的思维惯性--总想着做好万全准备才去做事 ...

随机推荐

  1. Linux上 &period;vimrc文件

    在Linux上面对VIM编辑器的格式的设置通常可以提升工作效率,下面对工作机器上的.vimrc文件的内容进行一总结,以备后续的查询 set smarttab set tabstop=4 set shi ...

  2. Windows平台配置免安装的MySQL

    1.下载 官网下载免安装文件(本文使用的是mysql-5.6.33-win32.zip)解压到E:\MySQL\mysql-5.6.33打开E:\MySQL\mysql-5.6.33\my-defau ...

  3. &lbrack;转&rsqb;StuQ 技能图谱(全套13张)

    程序开发语言综述.jpg 前端工程师必备技能.jpg 大数据工程师必备技能.jpg   安全工程师必备技能.jpg 嵌入式开发必备技能.jpg iOS开发工程师必备技能.jpg   移动无线测试工程师 ...

  4. 微信小程序手机预览请求不到数据(最后一条不明所以)

    本地开发调试小程序时,用手机预览需要有如下设置:1.微信开发者工具中设置:不校验安全域名.web-view 域名.TLS 版本以及 HTTPS 证书.这样在有网络请求的时候,就可以访问本地的服务器了, ...

  5. Dubbo2&period;6&period;5入门——管控台的安装

    首先去下载管控台:GitHub 然后解压到本地,截止到目前2019-01-18,最新管控台基于Dubbo2.7.0-SNAPSHOT版本,但是2.7.0还没有正式发布,不过影响不大. Dubbo Op ...

  6. Python全栈-magedu-2018-笔记6

    第三章 - Python 内置数据结构 bytes.bytearray Python3引入两个新类型 bytes 不可变字节序列 bytearray 字节数组 可变 bytes.bytearray 字 ...

  7. Expm 10&lowbar;1 带负权值边的有向图中的最短路径问题

    [问题描述] 对于一个带负权值边的有向图,实现Bellman-Ford算法,求出从指定顶点s到其余顶点的最短路径,并判断图中是否存在负环. package org.xiu68.exp.exp10; p ...

  8. resize2fs

    VPS是15G的..但是dh和fdisk显示不一样..求解 df:Filesystem Size Used Avail Use% Mounted onudev 236M 0 236M 0% /devt ...

  9. windows迁移linux问题集锦

    1)‘_wcsicmp’在此作用域中尚未声明 #ifdef WIN32#define _tcsicmp        _wcsicmp#else#define _tcsicmp        wcsc ...

  10. samtools 的应用

    1)sam转bam samtools view -bS in.sam > in.bam -b 意思使输出使BAM format -S 意思使输入使SAM,如果@SQ 缺剩, 要写-t