您的位置:首页 > 编程语言 > Java开发

JDK容器类里的迭代器模式学习

2014-03-19 20:07 543 查看
迭代器模式又叫游标模式,是提供一种方法用来访问容器内的元素,而不用暴露容器内部的实现。

这样有2个明显的好处:

1 容器内部的结构发生变化时,如果迭代器本身不发生变化,则外面使用迭代器的业务代码不用修改。

2 如果我们需要提供多于一种的迭代方式,则只需增加迭代器,不用修改容器类。

这2点也正好满足设计模式所要求的单一职责条例。

我们这里重点分析下JDK容器类(ArrayList和LinkedList)使用到的迭代器模式。

JDK提供的迭代器接口为:

public interface Iterator<E> {
boolean hasNext();//判断是否还有元素
E next();//返回下一个元素
void remove();//删除元数
}


ArrayList是以数组组织的List,其返回Iterator的接口如下:

public Iterator<E> iterator() {
return new Itr();//Itr是ArrayList内部类,其实现代码如下:
}
//按照Java的语法规则,内部类可以访问主类的属性和方法。
private class Itr implements Iterator<E> {
        int cursor;//当前元素的索引
        int lastRet = -1;//遍历过程中当前元素的前一个元素,-1表示没有这个元素
        int expectedModCount = modCount;//迭代器迭代过程中主类的结构(增加和删除元素等)变更次数
       
        //size表示主类里面元素个数,hasNext判断当前游标有没有超过元素个数,没超过,则可以继续遍历 
        public boolean hasNext() {
            return cursor != size;
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();//迭代期间主类的结构是否发生变化,如果发生变化,则抛出异常
            int i = cursor;
            if (i >= size)
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;//获得主类的元素
            if (i >= elementData.length)
                throw new ConcurrentModificationException();
            cursor = i + 1;//cursor增加1
            return (E) elementData[lastRet = i];//返回第cursor+1个元素
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
                checkForComodification();//迭代期间主类的结构是否发生变化,如果发生变化,则抛出异常           
            try {
                ArrayList.this.remove(lastRet);//删除前一个元素
                cursor = lastRet;//cursor指向前一个元素
                lastRet = -1;//表示目前没有前一个元素
                expectedModCount = modCount;//更正变更次数
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();//抛出并发修改异常
            }
        }
        //判断迭代期间主类的结构是否发生变化
        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
}

ArrayList还提供了一种双向迭代器,其实现如下:

//提供双向迭代器
public ListIterator<E> listIterator(int index) {
     if (index < 0 || index > size)//判断下标是否合法
            throw new IndexOutOfBoundsException("Index: "+index);
     return new ListItr(index);
}
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
super();
cursor = index;//当前元素小标
}
        //判断是否有前向元素
public boolean hasPrevious() {
return cursor != 0;
}
        //返回下一个指标
public int nextIndex() {
return cursor;
}
        //返回前一个元素下标
public int previousIndex() {
return cursor - 1;
}
        //返回前一个元素
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();//判断变更次数
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
        //设置元素值
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();

try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
        //增加元素
public void add(E e) {
checkForComodification();

try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}


LinkedList的以双向链表作为数据的组织方式,其内部迭代器的实现和ArrayList的类似,只是访问元素要遍历。

从上面的实现可以看出:

1 我们使用的时候,虽然不知道内部是如何存储的,但我们还是很方便的可以操作容器类的元素,如果内部数组组织方式变了,只要提供正确的迭代器,我们程序不需要修改。

2 增加一种新的迭代器也很方便,不需要对容器类进行修改,内部类的方式只是提供了实现方便,迭代器的实现完全可以独立出来。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息