从JDK看设计模式之行为模式:迭代器
2014-03-30 18:21
169 查看
迭代器模式
目录:
需求
使用
JDK源码
深入:为何要单独抽离出一个Iterator接口?
正文:
容器,在不同语言中都扮演着重要的角色,如Java中的List、Set和Map。迭代器模式是为容器而生的设计模式,是容器遍历的精灵。
第一部分:需求
1)使用者:针对多种容器,希望所有遍历方法一样,如容器更换代码无需变动
2)被使用者:不希望暴露内部实现细节,不同类型容器如何实现不需了解
第二部分:使用
迭代器的使用很简单,下面举例
例子1:ArrayList
迭代器的使用方式很简单,显示从容器中取得迭代器,之后根据迭代器的两个方法hasNext和next方法遍历。
例子2:更换ArrayList为HashSet
例子2更换的代码很少,只需要把ArrayList更换为HashSet即可。
例子3:更换HashSet为优先队列 PriorityQueue
依旧是简单的更换HashSet为PriorityQueue即可。
第三部分:JDK源码
使用上述代码,完全符合需求中的描述
1:对使用者来讲,容器遍历方法一样,改变容器类型后其他代码不需改变。
2:对被使用者容器来讲,不会暴露遍历方法实现的细节。
其实实现很简单,针对不同类型的容器,ArrayList、LinkedList、Vector或者HashSet, 他们各自遍历的方法肯定不一样需要在实现类中分别做实现 ,然后以同一种方式进行遍历。以List为例,无论是ArrayList或者LinkedList(前者是通过数组实现,后者是通过链表实现)均继承自在AbstractList,父类AbstractList中实现了iterator方法。
Itr类是一个内部类
代码很简单,在hasNext方法中,通过记录当前遍历cursor和容器大小比较,判断是否有下一个;在next方法中,通过get方法取得下一个元素。当然,get方法是根据不同的容器有不同实现,ArrayList是通过数组 下标定位,LinkedList是只能从开始通过指向下一个的引用遍历。
第四部分:深入
其实,总会有这样的疑问,既然不同的容器都要自己实现自己的遍历方法,那为什么不单独把hasNext方法 和next方法直接放到容器里,当成是容器的成员方法?干嘛还要抽象出一个Iterator接口呢?
有人这样说:在一个复杂系统里,接口的定义其实越细越好,有助于扩展、不同部门人合作等。很有道理,只是觉得并不足以说明放在容器里而不抽象出一个接口的必须性。
个人觉得原因在于:其实对于容器来讲,它还需要支持一个基本功能即多次遍历。如下
假设把hasNext方法和next方法当作容器的成员方法,类似在JDK中的实现,在容器里需要维护有一个变量(index)记录当前访问到第几个元素,以便和容器大小相比较,判断是否有下一个元素(hasNext方法),然后同样需要通过这个index访问元素(next方法)。
如果这个index是成员变量,那么该变量属于类的,不会在每次方法执行后自动消亡,第一次查找“iteye”的时候,在第3个元素中找到了它,index = 2 (下标从0 开始),我还需要查找容器是否包含字符串“csdn”,此时再次执行hasNext方法的时候,ind此时是从2开始的,而我们需要的是从头开始遍历,显然是不合要求
如果这个index放在方法体hasNext内部,每次在执行hasNext的时候确实能做到从0开始,但这样next方法又无法通过index得到该元素,因为index的作用域是hasNext方法。
放在一个类里就是一个很好的解决方案 ,每次遍历都new一个新的对象,该对象满足index从0开始,同时next方法和hasNext方法共享该变量。
在Java中每次执行Iterator方法的时候,总是new 一个新的Iterator的实现类,如HashMap中的KeyIterator。这样确保index每次都是从0开始,而且结束方法Iterator执行完毕后,hasNext方法和next方法仍能够共享变量index,因为此时这两个方法是在同一个类中。java是用内部类的方法来定义的,实际上用外围类也可以,只是内部类可以更好的隐藏细节。
目录:
需求
使用
JDK源码
深入:为何要单独抽离出一个Iterator接口?
正文:
容器,在不同语言中都扮演着重要的角色,如Java中的List、Set和Map。迭代器模式是为容器而生的设计模式,是容器遍历的精灵。
第一部分:需求
1)使用者:针对多种容器,希望所有遍历方法一样,如容器更换代码无需变动
2)被使用者:不希望暴露内部实现细节,不同类型容器如何实现不需了解
第二部分:使用
迭代器的使用很简单,下面举例
例子1:ArrayList
public static void main(String[] args) { Collection<String> collection = new ArrayList<String>(); collection.add("开源中国"); collection.add("csdn"); collection.add("Iteye"); collection.add("黑马"); collection.add("虎嗅网"); collection.add("环球军事"); // 取得容器的迭代器 Iterator<String> iterator = collection.iterator(); // 遍历 String content = ""; while (iterator.hasNext()) { content = iterator.next(); if(content == "Iteye"){ System.out.println("Bingo Iteye"); break; } } }
迭代器的使用方式很简单,显示从容器中取得迭代器,之后根据迭代器的两个方法hasNext和next方法遍历。
例子2:更换ArrayList为HashSet
public static void main(String[] args) { Collection<String> collection = new HashSet<String>(); collection.add("开源中国"); collection.add("csdn"); collection.add("Iteye"); collection.add("黑马"); collection.add("虎嗅网"); collection.add("环球军事"); // 取得容器的迭代器 Iterator<String> iterator = collection.iterator(); // 遍历 String content = ""; while (iterator.hasNext()) { content = iterator.next(); if(content == "Iteye"){ System.out.println("Bingo Iteye"); break; } } }
例子2更换的代码很少,只需要把ArrayList更换为HashSet即可。
例子3:更换HashSet为优先队列 PriorityQueue
public static void main(String[] args) { Collection<String> collection = new PriorityQueue<String>(); collection.add("开源中国"); collection.add("csdn"); collection.add("Iteye"); collection.add("黑马"); collection.add("虎嗅网"); collection.add("环球军事"); // 取得容器的迭代器 Iterator<String> iterator = collection.iterator(); // 遍历 String content = ""; while (iterator.hasNext()) { content = iterator.next(); if(content == "Iteye"){ System.out.println("Bingo Iteye"); break; } } }
依旧是简单的更换HashSet为PriorityQueue即可。
第三部分:JDK源码
使用上述代码,完全符合需求中的描述
1:对使用者来讲,容器遍历方法一样,改变容器类型后其他代码不需改变。
2:对被使用者容器来讲,不会暴露遍历方法实现的细节。
其实实现很简单,针对不同类型的容器,ArrayList、LinkedList、Vector或者HashSet, 他们各自遍历的方法肯定不一样需要在实现类中分别做实现 ,然后以同一种方式进行遍历。以List为例,无论是ArrayList或者LinkedList(前者是通过数组实现,后者是通过链表实现)均继承自在AbstractList,父类AbstractList中实现了iterator方法。
public Iterator<E> iterator() { return new Itr(); }
Itr类是一个内部类
private class Itr implements Iterator<E> { int cursor = 0; int lastRet = -1; int expectedModCount = modCount; public boolean hasNext() { return cursor != size(); } public E next() { checkForComodification(); try { E next = get(cursor); lastRet = cursor++; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } //其他省略 }
代码很简单,在hasNext方法中,通过记录当前遍历cursor和容器大小比较,判断是否有下一个;在next方法中,通过get方法取得下一个元素。当然,get方法是根据不同的容器有不同实现,ArrayList是通过数组 下标定位,LinkedList是只能从开始通过指向下一个的引用遍历。
第四部分:深入
其实,总会有这样的疑问,既然不同的容器都要自己实现自己的遍历方法,那为什么不单独把hasNext方法 和next方法直接放到容器里,当成是容器的成员方法?干嘛还要抽象出一个Iterator接口呢?
有人这样说:在一个复杂系统里,接口的定义其实越细越好,有助于扩展、不同部门人合作等。很有道理,只是觉得并不足以说明放在容器里而不抽象出一个接口的必须性。
个人觉得原因在于:其实对于容器来讲,它还需要支持一个基本功能即多次遍历。如下
public static void main(String[] args) { Collection<String> collection = new ArrayList<String>(); collection.add("开源中国"); collection.add("csdn"); collection.add("Iteye"); collection.add("黑马"); collection.add("虎嗅网"); collection.add("环球军事"); // 取得容器的迭代器 Iterator<String> iterator = collection.iterator(); // 遍历 String content = ""; while (iterator.hasNext()) { content = iterator.next(); if(content == "Iteye"){ System.out.println("Bingo Iteye"); break; } } //再次遍历 Iterator<String> iterator2 = collection.iterator(); while (iterator.hasNext()) { content = iterator2.next(); if(content == "csdn"){ System.out.println("Bingo csdn"); break; } } }
假设把hasNext方法和next方法当作容器的成员方法,类似在JDK中的实现,在容器里需要维护有一个变量(index)记录当前访问到第几个元素,以便和容器大小相比较,判断是否有下一个元素(hasNext方法),然后同样需要通过这个index访问元素(next方法)。
如果这个index是成员变量,那么该变量属于类的,不会在每次方法执行后自动消亡,第一次查找“iteye”的时候,在第3个元素中找到了它,index = 2 (下标从0 开始),我还需要查找容器是否包含字符串“csdn”,此时再次执行hasNext方法的时候,ind此时是从2开始的,而我们需要的是从头开始遍历,显然是不合要求
如果这个index放在方法体hasNext内部,每次在执行hasNext的时候确实能做到从0开始,但这样next方法又无法通过index得到该元素,因为index的作用域是hasNext方法。
放在一个类里就是一个很好的解决方案 ,每次遍历都new一个新的对象,该对象满足index从0开始,同时next方法和hasNext方法共享该变量。
在Java中每次执行Iterator方法的时候,总是new 一个新的Iterator的实现类,如HashMap中的KeyIterator。这样确保index每次都是从0开始,而且结束方法Iterator执行完毕后,hasNext方法和next方法仍能够共享变量index,因为此时这两个方法是在同一个类中。java是用内部类的方法来定义的,实际上用外围类也可以,只是内部类可以更好的隐藏细节。
相关文章推荐
- java的一些基础知识
- Spring声明式事务配置管理方法
- Java开发环境配置
- Spring源代码解析(二):IoC容器在Web容器中的启动
- Spring3.x企业应用开发实战-Spring+Hibernat架构分析
- JAVA中的IO 流们
- Spring3.x企业应用开发实战-Spring+Hibernat架构分析
- java编程规范之java注释规范
- java编程规范之java注释规范
- java编程规范之java注释规范
- java8+eclipse+maven开发环境搭建
- 第三章 DispatcherServlet详解 ——跟开涛学SpringMVC
- eclipse-以指定颜色高亮显示选中关键字
- Java Hash类 取值方式
- Bonmin eclipse can not find file *.so.4
- Java接口设计模式
- 从Decorator,Adapter模式看Java的IO库
- java类调用简单存储过程--小白同志可以看看@我也是小白
- Java虚拟机进阶之二:运行时数据区内存划分
- Java工程师学习列表