- 浏览: 409204 次
- 性别:
- 来自: 秦皇岛
最新评论
-
prayjourney:
了解了,讲的不错
DataInputStream和DataOutputStream类 -
niunianss:
将字节退回的时候,需要添加判断,最后一个字符是英文时,实际数组 ...
PushbackInputStream -
cctt_1:
不要误人子弟,那根本就不是 解释器模式!!!那是Composi ...
Interpreter(解释器)模式 -
java-大神:
[i][i]引用引用引用引用[/img][/img][/img ...
BufferedReader和BufferedWriter -
百合不是茶:
你的程序在文件输入输出流中传入agrs[0]时,会报错越界 ...
DataInputStream和DataOutputStream类
当通过添加新类型的集合来扩展代码库时,你也许发现有必要通过增加迭代器进一步扩展你的扩展类.本章讨论对组合结构进行迭代的特殊情况.除了对新类型的集合进行迭代之外,在多线程环境下进行迭代会导致一些值得关注的问题.迭代看起来简单,但是其中还有很多需要探讨的地方,并不成熟.
Iterator模式的意图在于为开发人员提供一种顺序访问集合元素的方法.
1.常规迭代:
Java对迭代提供很多支持:
(1)For,while和repeat循环,通常使用整数的索引;
(2)Enumeration类(在java.util中);
(3)Iterator类(也存在于java.util中),支持JDK1.2中的集合;
(4)JDK 1.5中增加的对循环的扩展(foreach).
我们将使用Iterator类作为本章介绍重点,本部分主要关注扩展的for循环.
Iterator类有三个方法:hasNext(),next()和remove().如果Iterator不支持remove()操作,将会抛出UnsupportedOperationException异常.
扩展的for循环的形式如下:
for(Type element:collection)
此处没有必要把element映射到特定类型, 这些操作都是隐含处理的.上述语句也适合于处理数组.提供扩展的for循环的类必须实现Iterable接口,并提供一个iterator()方法.
下面代码演示Iterator类和改进的for循环:
package app.iterator; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ShowForeach { public static void main(String[] args){ ShowForeach example = new ShowForeach(); example.showIterator(); System.out.println(): example.showForeach(); } public void showIterator(){ List names = new ArrayList(); names.add("Fuser:1101"); names.add("StarPress:991"); names.add("Robot:1"); System.out.println("JDK 1.2-style Iterator:"); for(Iterator it = names.iterator();it.hasNext();){ String name = (String)it.next(); System.out.println(name); } } public void showForeach(){ List<String> names = new ArrayList<String>(); names.add("Fuser:1101"); name.add("StarPress:991"); names.add("Robot:1"); System.out.println("JDK 1.5-style Extended For Loop"); for(String name:names) System.out.println(name); } }
运行结果相同.
到目前为止,Oozinoz应用程序继续使用老式的Iterator类;在保证客户拥有更新的编译器之前,我们不能更新该类.无论如何,你都可以从现在开始学习扩展的for循环.
2.线程安全的迭代:
强大的应用程序经常使用线程来实现同时执行任务的操作.尤其值得关注的是,我们经常把那些耗时的任务放在后台处理,这样就不会影响前台GUI的显示效果.线程化是非常有用的,但同时也很危险.很多应用程序崩溃,其主要原因是线程化任务之间没有很好地协作.当多线程应用程序失效时,对集合进行迭代的方法肯定是潜在的故障源.
java.util.collections中的集合类通过提供synchronized()方法来量度线程安全.从本质上看,该方法可返回底层集合的版本,以此防止两个线程同时去更改集合.
集合和迭代器共同检测在迭代过程中列表是否变换,也就是说,列表是否被同步.为查看这个效果,假设Oozinoz Factory单例可以告诉我们在特定时间哪些机器是正常的,并可显示"正常的"机器列表.app.iterator.concurrent包中的范例代码把这个列表硬编码在方法upMachineNames()中.
如下程序显示当前处于正常状态的机器列表,但是当程序显示这个列表时模拟机器被逐个发现的状况:
package app.iterator.concurrent; import java.util.*; public class ShowConcurrentIterator implements Runnable { private List list; protected static List upMachineNames(){ return new ArrayList(Arrays.asList(new String[]{ "Mixer 1201","ShellAssembler1301", "StarPress1401","UnloadBuffer1501"})); } public static void main(String[] args){ new ShowConcurrentIterator().go(); } protected void go(){ list = Collections.synchronizedList(upMachineNames()); Iterator iter = list.iterator(); int i = 0; while(iter.hasNext()){ i++; if(i==2){ new Thread(this).start(); try{Thread.sleep(100);} catch(InterruptedException ignored){} } System.out.println(iter.next()); } } public void run(){ list.add(0,"Fuser1101"); } }
上述代码中main()方法构造该类的一个实例,并调用go()方法.该方法会对正常机器列表进行迭代处理,注意构造该列表的同步版本.代码模拟当该方法迭代处理这个列表时,其他机器逐个被发现的状况.run()方法修改本列表,在单个线程中运行.
ShowConcurrentIterator程序会输出一个或者两个机器,然后崩溃:
D:\Java_Test>java app.iterator.concurrent.ShowConcurrentIterator
Mixer 1201
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:4
49)
at java.util.AbstractList$Itr.next(AbstractList.java:420)
at app.iterator.concurrent.ShowConcurrentIterator.go(ShowConcurrentItera
tor.java:29)
at app.iterator.concurrent.ShowConcurrentIterator.main(ShowConcurrentIte
rator.java:15)
该程序之所以运行失败,是因为迭代器对象检测到迭代过程中列表已经被改变了.我们不必使用新线程就可以观察到这种现象.你可以创建一个程序,使其仅仅实现在迭代循环中更改处理集合时就崩溃.不过,在多线程应用环境中,当迭代器在遍历一个列表时,列表更加有可能会被意外的修改.
对于列表迭代处理,我们可以开发线程安全的方法;首先需要注意到,ShowConcurrent-Iterator程序崩溃的原因是它使用迭代器.使用for循环对同步列表进行迭代处理不会触发程序以前遇到的异常,但是仍旧会出现问题:
package app.iterator.concurrent; import java.util.*; public class ShowConcurrentFor implements Runnable { private List list; protected static List upMachineNames(){ return new ArrayList(Arrays.asList(new String[]{ "Mixer 1201","ShellAssembler1301", "StarPress1401","UnloadBuffer1501"})); } public static void main(String[] args){ new ShowConcurrentFor().go(); } protected void go(){ System.out.println("This version lets new things " + "be added in concurrently:"); list = Collections.synchronizedList(upMachineNames()); display(); } private void display(){ for(int i=0;i<list.size();i++){ if(i==1) { //simulate wake-up new Thread(this).start(); try{Thread.sleep(100);} catch(InterruptedException ignored){} } System.out.println(list.get(i)); } } //在一个单独的进程中向列表中添加一个元素 public void run(){ list.add(0,"Fuser1101"); } }
运行本程序会得到如下输出:
D:\Java_Test>java app.iterator.concurrent.ShowConcurrentFor
This version lets new things be added in concurrently:
Mixer 1201
Mixer 1201
ShellAssembler1301
StarPress1401
UnloadBuffer1501
突破题:请解释ShowConcurrentFor程序的输出结果:
答:display()方法启动可以在任何时间被唤醒的新线程,尽管sleep()调用有助于确保run()过程在display()方法正在睡眠时运行.输出结果表明,在特定运行过程中,display()方法通过一次迭代保留了对CPU的控制权,并打印列表中索引0的数据项:
Mixer1201
在这个时候,Second线程被唤醒,该线程将Fuser1101放在了列表的开头,并将其他机器名称下移一位.特别是将Mixer1201从索引0的位置移到了索引1的位置.
当主线程重新获得控制权之后,display()方法将继续输出列表中的其余内容,即从索引1开始到列表结束:
Mixer 1201
ShellAssembler1301
StarPress1401
UnloadBuffer1501
我们已经看到这个程序的两个版本:一个会崩溃,另一个会产生不正确输出.两个程序的输出结果都不理想,所以,需要考虑使用其他方法来保护被迭代的列表.
在多线程应用程序中,有两种方法可以实现对集合的安全迭代处理.两个方法都涉及使用对象,有时被称作互斥量(mutex),参与竞争该对象控制权的线程将共享该对象.其中一个方法要求,每个线程在访问集合前都必须先获得对集合互斥量锁的控制.下面代码说明了这个方法:
package app.iterator.concurrent; import java.util.*; public class ShowConcurrentMutex implements Runnable { private List list; protected static List upMachineNames(){ return new ArrayList(Arrays.asList(new String[] { "Mixer1201","ShellAssembler1301","StarPress1401","UnloadBuffer1501"})); } public static void main(String[] args){ new ShowConcurrentMutex().go(); } protected void go(){ System.out.println("This version synchronizes properly:"); list = Collections.synchronizedList(upMachineNames()); synchronized(list){ display(); } } private void display(){ for(int i=0;i<list.size();i++){ if (i==1) { //simulate wake-up new Thread(this).start(); try { Thread.sleep(100); } catch (InterruptedException ignored) { } } System.out.println(list.get(i)); } } public void run(){ synchronized (list){ list.add(0,"Fuser1101"); } } }
输出结果如下:
D:\Java_Test>java app.iterator.concurrent.ShowConcurrentMutex
This version synchronizes properly:
Mixer1201
ShellAssembler1301
StarPress1401
UnloadBuffer1501
上面输出的结果与run()方法插入新对象之前的结果一致.程序的输出是一致的,没有重复内容,原因是本程序的处理逻辑要求run()方法等待display()方法中迭代完成之后再执行.尽管结果正确,但是设计思路有点不合适:我们通常不希望一个线程对集合进行迭代时,其他线程被阻塞.
替代解决方式是在一个互斥操作中对集合进行克隆,然后对该克隆体进行迭代,这样做的效率会更高.克隆集合通常比在遍历前等待其他线程完成对集合操作所花费的时间少得多.但是克隆一个集合,然后对克隆体进行迭代,这样可能会带来一些问题.
ArrayList的clone()方法会产生一个浅复制(shallow copy):它们仅仅创建一个引用原来对象的新集合.当另外一个线程改变了我们所使用的对象的时候,对克隆体进行迭代还是会导致失败.但是在某些情况下,这种危险是很小的.例如,如果我们仅仅期望显示机器名的列表,在迭代克隆体的时候,机器名被其他线程修改的可能性极小,而且修改机器名也是不合理的.
总的来说,我们已经介绍在多线程环境下迭代处理列表的四种方法.其中两种方法使用synchronized()方法,但是程序要么崩溃,要么产生不正确结果.后两种方法使用锁和克隆机制,也许会产生正确结果,但是也存在不同程度的负面影响.
突破题:请解释为什么不使用synchronized()方法,为什么不使用基于锁的方法?
答:反对使用syncrhonized()方法的一个论点是:如果使用for循环进行迭代,synchronized()方法会产生错误结果;如果没有捕获InvalidOperationException异常,则程序会崩溃.
反对使用基于 锁的方法的论点是:提供线程安全迭代的设计思路依赖于访问集合的线程间的合作. 基于锁的方法关键的一点是线程不能协作.
内置于Java语言的synchronized()方法和锁机制都不能方便多线程程序开发.如想进一步了解并发程序设计,请参考《Java并发编程》[Lea,2000]。
Java及其类库为多线程环境的迭代提供坚实的支持,但是这种支持并没有降低并发设计的复杂性。Java类库对自身的很多集合提供强有力的迭代支持,但是对于开发者自己引入的集合类型,也许有必要同时引入对应的迭代器。
发表评论
-
工厂方法模式示例
2009-11-27 14:34 1406//工厂方法模式示例: package innerclass ... -
Flyweight(享元)模式
2009-05-15 21:24 1182Flyweight模式可实现客户 ... -
Builder(生成器)模式
2009-05-15 21:22 1624当构造某对象时,也 ... -
责任链(Chain of Responsibility)模式
2009-05-15 21:18 1825面向对象开发 ... -
Prototype(原型)模式
2009-05-15 21:17 1489Prototype模式不通过实例化类来创建一个新的未初始化的实 ... -
Proxy(代理)模式
2009-05-15 21:14 1528Proxy(代理)模式 ... -
Mediator(中介者)模式
2009-05-15 21:14 1478通常,面向对象的软 ... -
Observer(观察者)模式一
2009-05-15 21:13 1561Observer模式的宗旨是在多个对象之间定义一对多的关系,以 ... -
责任型模式
2009-05-15 21:12 1013对象的责任类似于 ... -
Bridge(桥接模式)
2009-05-15 21:11 1420Bridge模式着重考虑如何实现对具体实现的抽象(ab ... -
Facade外观模式
2009-05-15 21:11 29041.Facade模式就是一个功能介于包和完整应用程序之 ... -
Composite(组合)模式
2009-05-15 21:07 1774Composite模式涉及的是一组对象,其中有些 ... -
Memento(备忘录)模式
2009-05-15 21:07 2203有时候,我们需要创建的对象曾经在系统中出现过。这种情形可能出现 ... -
操作型模式简介
2009-05-15 21:01 1158我们只有弄清楚诸如算法、多态性、方法和操作等术语的不同意 ... -
Template Method(模板方法)模式
2009-05-15 21:01 1081普通方法的方法体 ... -
Template Method二
2009-05-15 20:59 1233Template Method模式类似于Adapter模 ... -
State(状态)模式
2009-05-15 20:58 1441对象的状态是其属性当 ... -
Strategy(策略)模式
2009-05-15 20:55 1076算法是定义好的过程, ... -
Strategy(策略)模式二
2009-05-15 20:54 13592.重构为Strategy模式: ... -
Command(命令)模式
2009-05-15 20:52 1544让方法运行的最常见 ...
相关推荐
C#面向对象设计模式 (行为型模式) Iterator 迭代器模式 视频讲座下载
在这里与各位分享本人从网络上下载的C#面向对象设计模式纵横谈系列视频,共有25节,除了第一节需要各位贡献一点资源分以作为对本人上传资源的回馈,后面的其他资源均不... 这是第18节:行为型模式Iterator迭代器模式
首先,迭代器模式包含两个主要角色:聚合对象(Aggregate)和迭代器(Iterator)。聚合对象持有元素的集合,并提供创建迭代器的方法。迭代器则负责遍历聚合对象中的元素,它有一个或多个方法用于移动到下一个元素,...
### Iterator迭代器详解 #### 一、Iterator简介与概念 在Java编程语言中,`Iterator`接口是一个重要的组件,它提供了遍历集合的基本方法。`Iterator`的主要作用是在不暴露集合内部结构的情况下,顺序访问集合中的...
例如,在Java中,`Iterable`接口和`Iterator`接口就是实现迭代器模式的关键。`Iterable`接口定义了获取迭代器的方法`iterator()`,而`Iterator`接口提供了`hasNext()`和`next()`方法,分别用于检查是否还有下一个...
迭代器模式是软件设计模式中的一种行为模式,它在C#等面向对象编程语言中有着广泛的应用。这个模式的主要目标是允许用户遍历一个聚合对象(如数组、集合或列表)的所有元素,而无需了解底层的实现细节。下面将详细...
在Java编程语言中,迭代器模式(Iterator Pattern)是一种常用的设计模式,用于顺序访问集合对象中的元素,而无需暴露其底层表示。这种模式提供了一种方法来访问一个聚合对象的元素,而无需暴露该对象的内部结构。在...
迭代器模式是一种设计模式,属于行为设计模式,它允许我们顺序访问聚合对象的元素,而无需暴露其底层表示。在Java、C#等面向对象语言中,迭代器模式被广泛应用于容器类,如ArrayList、LinkedList等,使得我们可以...
迭代器模式(IteratorPattern)是设计模式中的一种行为模式,它提供了一种顺序访问聚合对象元素的方法,同时又不暴露其底层表示。这种模式允许我们遍历集合对象的元素,而无需暴露其内部结构。在Java、C#等面向对象...
迭代器模式(Iterator Pattern)是Java设计模式中的行为模式之一,它提供了一种方法来顺序访问聚合对象的元素,而又不暴露其底层表示。在Java中,迭代器模式被广泛应用于集合类,如ArrayList、LinkedList等,通过...
迭代器模式是软件开发中广泛使用的设计模式之一,特别是在处理聚合数据结构如数组、列表和集合时。迭代器模式可以提供一种统一的方式来遍历这些结构中的元素,从而使得开发者不需要关心聚合对象的内部实现细节。通过...
迭代器模式是一种设计模式,它在软件工程中扮演着重要的角色,特别是在处理集合或容器类对象的遍历操作时。这种模式提供了一种方法来顺序访问聚合对象的元素,而无需暴露其底层表示。在Java、C#等面向对象语言中,...
迭代器模式是软件设计模式中的一种行为模式,它允许我们顺序访问聚合对象的元素,而无需暴露其底层表示。在Java、C#等面向对象语言中,迭代器模式被广泛应用于容器类,如ArrayList、LinkedList等,使得我们可以方便...
迭代器模式是一种行为设计模式,它提供了一种方法来顺序访问聚合对象的元素,而无需暴露其底层表示。在Java中,迭代器模式是通过接口实现的,这使得我们可以遍历任何实现了`Iterable`接口的对象,例如集合框架中的`...
在Java、C#等面向对象语言中,迭代器模式的应用非常广泛,例如Java中的`Iterable`接口和`Iterator`接口,C#中的`IEnumerable`接口和`IEnumerator`接口。这些接口为实现迭代器模式提供了标准的方式。 迭代器模式的...
迭代器模式(Iterator Pattern)是设计模式中的一种行为模式,它允许顺序访问一个聚合对象中的各个元素,而又不需要暴露该对象的内部表示。迭代器模式提供了一种方法,可以顺序地访问一个聚合对象中的各个元素,而又...
迭代器模式是软件设计模式中的行为模式之一,它在C++编程中有着广泛的应用。这个模式提供了一种方法来顺序访问聚合对象的元素,而无需暴露其底层表示。通过迭代器,用户可以遍历集合中的所有元素,而无需知道如何...
在这个主题中,我们主要探讨了如何利用组合模式(Composite Pattern)构建二叉树,并通过迭代器模式(Iterator Pattern)来实现对树的遍历,包括前序、中序和后序遍历。这些是设计模式中的经典应用,对于理解和掌握...