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

设计模式——迭代器模式

2016-04-26 10:49 399 查看
上次我们讲了下观察者模式《设计模式——观察者模式》,这次我们来看下迭代器模式。迭代器模式我们自己可能很少直接的使用,但是我们却经常在间接地使用,Java集合类就用到了这个模式,这个可以通过他们的源码来验证。

我们知道对容器对象的访问必然会涉及到遍历操作,一般情况下,我们的做法是将遍历的方法封装在容器中,但是这样会带来新的问题:容器类不仅要维护自身内部的数据元素,还要对外提供遍历的接口方法,这样不但显得类很臃肿,同时也违背了抽象编程原则。为了解决上面的缺点,我们可以采用迭代器模式,将遍历集合的操作与集合的底层实现的结构分离。

一,定义:

迭代器模式:提供一种方法顺序访问一个容器中的各个元素,而又不需要暴露该对象的内部表示。

二,UML类图

迭代器模式UML类图如下:



Iterator:迭代器接口,定义了两个方法。next()方法返回当前位置的元素并将位置移至下一位;hasNext()判断是否还有下一个元素。

ConcreteIterator:具体迭代器,实现了迭代器接口(Iterator)。

Aggregate:容器接口,定义了容器的基本操作:add()添加元素,remove()删除元素,iterator()获取容器的迭代器。

ConcreteAggregate:具体容器类。

三,实例

上面我们提到,Java集合类就用到迭代器模式,这里我们就以集合的源码来举例说明迭代器模式的使用。

首先,我们看下java.util.Iterator,这就是迭代器接口,它的定义如下:

public interface Iterator<E> {
/**
* Returns true if there is at least one more element, false otherwise.
* @see #next
*/
public boolean hasNext();

/**
* Returns the next object and advances the iterator.
*
* @return the next object.
* @throws NoSuchElementException
*             if there are no more elements.
* @see #hasNext
*/
public E next();

/**
* Removes the last object returned by {@code next} from the collection.
* This method can only be called once between each call to {@code next}.
*
* @throws UnsupportedOperationException
*             if removing is not supported by the collection being
*             iterated.
* @throws IllegalStateException
*             if {@code next} has not been called, or {@code remove} has
*             already been called after the last call to {@code next}.
*/
public void remove();
}


在Iterator 中定义了三个方法,比我们上面UML图中多了一个remove()方法,此方法用于将调用next()返回的对象从容器中移除。方法的定义不是重点,我们要学习的是迭代器模式的这种思想。接下来,我们再看下java.util.Collection这个接口,此接口对应的就是上面UML图中的Aggregate接口。Collection接口部分定义如下:

public interface Collection<E> extends Iterable<E> {

/**
* Attempts to add {@code object} to the contents of this
* {@code Collection} (optional).
*
* After this method finishes successfully it is guaranteed that the object
* is contained in the collection.
*
* If the collection was modified it returns {@code true}, {@code false} if
* no changes were made.
*
* An implementation of {@code Collection} may narrow the set of accepted
* objects, but it has to specify this in the documentation. If the object
* to be added does not meet this restriction, then an
* {@code IllegalArgumentException} is thrown.
*
* If a collection does not yet contain an object that is to be added and
* adding the object fails, this method <i>must</i> throw an appropriate
* unchecked Exception. Returning false is not permitted in this case
* because it would violate the postcondition that the element will be part
* of the collection after this method finishes.
*
* @param object
*            the object to add.
* @return {@code true} if this {@code Collection} is
*         modified, {@code false} otherwise.
*
* @throws UnsupportedOperationException
*                if adding to this {@code Collection} is not supported.
* @throws ClassCastException
*                if the class of the object is inappropriate for this
*                collection.
* @throws IllegalArgumentException
*                if the object cannot be added to this {@code Collection}.
* @throws NullPointerException
*                if null elements cannot be added to the {@code Collection}.
*/
public boolean add(E object);

/***
* 其他方法省略,有兴趣的可以看下源码
*/

/**
* Returns an instance of {@link Iterator} that may be used to access the
* objects contained by this {@code Collection}. The order in which the elements are
* returned by the iterator is not defined. Only if the instance of the
* {@code Collection} has a defined order the elements are returned in that order.
*
* @return an iterator for accessing the {@code Collection} contents.
*/
public Iterator<E> iterator();

/**
* Removes one instance of the specified object from this {@code Collection} if one
* is contained (optional). The element {@code elem} that is removed
* complies with {@code (object==null ? elem==null : object.equals(elem)}.
*
* @param object
*            the object to remove.
* @return {@code true} if this {@code Collection} is modified, {@code false}
*         otherwise.
* @throws UnsupportedOperationException
*                if removing from this {@code Collection} is not supported.
* @throws ClassCastException
*                if the object passed is not of the correct type.
* @throws NullPointerException
*                if {@code object} is {@code null} and this {@code Collection}
*                doesn't support {@code null} elements.
*/
public boolean remove(Object object);


这里只列举了Collection的三个方法,其他的方法我们先不讨论,感兴趣的同学可以看下java.util.Collection 的源码。到目前为止,迭代器接口、容器接口我们都列举出来了,现在我们看下他们的实现类。Collection的实现类有很多,有ArrayList、LinkedList、HashSet等。这里我们以常用的ArrayList来分析,ArrayList中的add(),remove()方法的具体实现,我们不去讨论,直接看iterator()方法

@Override public Iterator<E> iterator() {
return new ArrayListIterator();
}

private class ArrayListIterator implements Iterator<E> {
/** Number of elements remaining in this iteration */
private int remaining = size;

/** Index of element that remove() would remove, or -1 if no such elt */
private int removalIndex = -1;

/** The expected modCount value */
private int expectedModCount = modCount;

public boolean hasNext() {
return remaining != 0;
}

@SuppressWarnings("unchecked") public E next() {
ArrayList<E> ourList = ArrayList.this;
int rem = remaining;
if (ourList.modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (rem == 0) {
throw new NoSuchElementException();
}
remaining = rem - 1;
return (E) ourList.array[removalIndex = ourList.size - rem];
}

public void remove() {
Object[] a = array;
int removalIdx = removalIndex;
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
if (removalIdx < 0) {
throw new IllegalStateException();
}
System.arraycopy(a, removalIdx + 1, a, removalIdx, remaining);
a[--size] = null;  // Prevent memory leak
removalIndex = -1;
expectedModCount = ++modCount;
}
}


从代码中可以看出:iterator()方法返回了一个ArrayListIterator对象,而这个ArrayListIterator是ArrayList的内部类,它实现了Iterator接口。至于三个方法的具体实现,它们会涉及到ArrayList底层实现的内容,这里也不做讨论了,有时间以后再写下有关ArrayList底层实现的原理的文章。

最后总结下,迭代器模式让我们能访问容器中每个元素,而又不暴露其内部的表示;这种模式把遍历的任务放在了迭代器上,而不是容器上,这样简化了容器的接口和实现,也让责任各得其所。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息