设计模式——迭代器模式

上次我们讲了下观察者模式《设计模式——观察者模式》,这次我们来看下迭代器模式。迭代器模式我们自己可能很少直接的使用,但是我们却经常在间接地使用,Java集合类就用到了这个模式,这个可以通过他们的源码来验证。
我们知道对容器对象的访问必然会涉及到遍历操作,一般情况下,我们的做法是将遍历的方法封装在容器中,但是这样会带来新的问题:容器类不仅要维护自身内部的数据元素,还要对外提供遍历的接口方法,这样不但显得类很臃肿,同时也违背了抽象编程原则。为了解决上面的缺点,我们可以采用迭代器模式,将遍历集合的操作与集合的底层实现的结构分离。

一,定义:

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

二,UML类图

迭代器模式UML类图如下:
设计模式——迭代器模式_第1张图片

  • 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底层实现的原理的文章。

最后总结下,迭代器模式让我们能访问容器中每个元素,而又不暴露其内部的表示;这种模式把遍历的任务放在了迭代器上,而不是容器上,这样简化了容器的接口和实现,也让责任各得其所。

你可能感兴趣的:(java,设计模式,迭代器)