Java容器源码分析—迭代器

迭代器

  • 概述
  • 迭代器模式
    • 1、定义与结构
    • 2、迭代器实现
    • 3、适用情况
  • Iterator迭代器与Iterable接口
  • ListIterator
    • 1、简述
    • 2、与Iterator区别
  • 常见问题

概述

迭代器的好处是封装容器的内部实现细节,对于不同的集合,可以提供统一的遍历方式,简化客户端的访问和获取容器内的数据;

迭代器模式


1、定义与结构

迭代器(Iterator)模式:提供一种方法访问一个容器(container)对象中的各个元素,而又不需暴露该容器对象的内部细节;

组成
Iterator:负责定义访问和遍历元素的接口;
Concrete Iterator:要实现迭代器接口,并要 记录遍历中的当前位置;
Container:负责定义创建具体迭代器角色的接口;
Concrete Container:实现创建具体迭代器角色的接口;

结构图
Java容器源码分析—迭代器_第1张图片
迭代器模式在客户端与容器之间加入了迭代器角色。迭代器角色的加入,就可以很好的避免容器内部细节的暴露,而且也使得设计符合 单一职责原则


2、迭代器实现

看一下迭代器在Java Cellection中的迭代器实现:

//迭代器角色,仅仅定义了遍历接口
public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}
//容器角色,这里以 List 为例,间接实现了 Iterable 接口
public interface Collection<E> extends Iterable<E> {
    ...
    Iterator<E> iterator();
    ...
}
public interface List<E> extends Collection<E> {}

//具体容器角色,便是实现了 List 接口的 ArrayList 等类。为了突出重点这里指罗列和迭代器相关的内容
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
…… 
//这个便是负责创建具体迭代器角色的工厂方法
public Iterator<E> iterator() {
 return new Itr();
}

//具体迭代器角色,它是以内部类的形式出来的。 AbstractList 是为了将各个具体容器角色的公共部分提取出来而存在的。
//作为内部类的具体迭代器角色
private class Itr implements Iterator<E> {
 int cursor = 0;
 int lastRet = -1;
  //集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性. ArrayList 中存在 modCount 属性,增删操作都会使 modCount++,
//通过两者的对比,迭代器可以快速的知道迭代过程中是否存在 list.add() 类似的操作,存在的话快速失败!
 int expectedModCount = modCount;  

 public boolean hasNext() {
  return cursor != size();
 }

 public Object next() {
  checkForComodification();   //快速失败机制
  try {
   Object next = get(cursor);
   lastRet = cursor++;
   return next;
  } catch(IndexOutOfBoundsException e) {
   checkForComodification();   //快速失败机制
   throw new NoSuchElementException();
  }
 }

3、适用情况

迭代器模式给容器带来的好处:

  1. 支持以不同的方式遍历一个容器角色。根据实现方式的不同,效果上会有差别;
  2. 简化了容器的接口;
  3. 简化了遍历方式;
  4. 可以提供多种遍历方式;
  5. 对同一个容器对象,可以同时进行多个遍历;
  6. 封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心;
  7. 在 Java Collection 中,迭代器提供一种快速失败机制;

Iterator迭代器与Iterable接口


思考一下遍历一个数组的方法:

for(int i=0; i<array.size(); i++) { ... get(i) ... } 

遍历一个HashSet呢

while((e=e.next())!=null) { ... e.data() ... } 

客户端需要实现知道集合的类型,访问代码和集合之间是紧密耦合的,导致每一种集合对应一种遍历方法,无法复用代码;

为解决以上问题,Iterator模式总是用同一种逻辑来遍历集合

通过Iterator,客户端从不直接和集合类打交道,它总是控制Iterator,向它发送”向前”,”向后”,”取当前元素”的指令,就可以间接遍历整个集合

for(Iterator it = c.iterator();it.hasNext();){...}

典型代码如下:

for(Iterator it = c.iterator(); it.hasNext(); ) { Object o = it.next(); // 对o的操作... } 

多态迭代 : 每一种集合类返回的 Iterator 具体类型可能不同,Array 可能返回 ArrayIterator,Set 可能返回 SetIterator,Tree 可能返回 TreeIterator,但是它们都实现了 Iterator 接口 ;


ListIterator

1、简述

ListIterator 没有当前元素;它的光标位置始终位于调用 previous() 所返回的元素和调用 next() 所返回的元素之间;

2、与Iterator区别

Iterator 和 ListIterator 主要区别有:

  • ListIterator 有 add()方法,可以向 List 中添加对象,而 Iterator 不能 ;
  • ListIterator 和 Iterator 都有 hasNext()和next()方法,可以实现顺序向后遍历。但是
    ListIterator 有 hasPrevious() 和 previous() 方法,可以实现逆向(顺序向前)遍历,而 Iterator 就不可以 ;
  • ListIterator 可以利用 nextIndex() 和 previousIndex() 定位当前的索引位置,而 Iterator没有此功能 ;
  • ListIterator 可以通过 listIterator() 方法和 listIterator(int index) 方法获得,而
    Iterator 只能由 iterator() 方法获得 ;
  • 二者都可以实现删除对象,但是ListIterator可以使用set()方法实现对象的修改。Iterator仅能遍历,不能修改。因为ListIterator的这些功能,可以实现对LinkedList, ArrayList等List数据结构的操作;

常见问题

1、Iterator 和ListIterator 的区别是什么?
上面说了;

2、Enumeration 接口和Iterator 接口的区别有哪些?
Enumeration 速度是Iterator 的2 倍,同时占用更少的内存。但是,Iterator 远远比Enumeration 安全,因为其他线程不能够修改正在被iterator 遍历的集合里面的对象。同时,Iterator 允许调用者删除底层集合里面的元素,这对Enumeration 来说是不可能的。

你可能感兴趣的:(Java容器,Iteraor,listIterator)