Design Patterns—Iterator Patterns

1 Introduction

In practical applications, we try to access all elements in an aggregate object without caring about the internal conditions of the elements. For example, a bus conductor sells tickets, no matter whether the person on the bus is a man, a woman, the elderly or children, as long as you get on the bus, you need to buy a ticket. This corresponds to the iterator pattern.

2 Definitions

Iterator pattern: Provides an object to sequentially access a series of data in an aggregated object without exposing the internal representation of the aggregated object.

3 Structure and Implementation

The iterator pattern is implemented by separating the traversal behavior of the aggregate object and abstracting it into an iterator class. Its purpose is to allow external code to transparently access the internal data of the aggregate without exposing the internal structure of the aggregate object.

The iterator pattern mainly includes the following roles:

  1. Abstract Aggregate role: Defines the interface for storing, adding, deleting aggregate objects, and creating iterator objects.
  2. Concrete Aggregate role: implements an abstract aggregate class that returns an instance of a concrete iterator.
  3. Abstract iterator (Iterator) role: define the interface for accessing and traversing aggregate elements, usually including hasNext(), first(), next() and other methods.
  4. Concrete iterator role: implement the methods defined in the abstract iterator interface, complete the traversal of the aggregated object, and record the current position of the traversal.

The structure diagram is shown in the following figure (the picture is from reference 2):

4 Advantages and disadvantages 

4.1 Advantages

  1. Access the contents of an aggregate object without exposing its internal representation.
  2. The traversal task is done by iterators, which simplifies the aggregation class.
  3. It supports traversing an aggregate in different ways, and you can even subclass iterators to support new traversals.
  4. It is very convenient to add new aggregate classes and iterator classes without modifying the original code.
  5. Good encapsulation, providing a unified interface for traversing different aggregation structures.

4.2 Disadvantages

  1. The number of classes is increased, which increases the complexity of the system to a certain extent.

5 Application scenarios

  1. When there is a need to provide multiple ways of traversing an aggregated object.
  2. When it is necessary to provide a unified interface for traversing different aggregate structures.
  3. When accessing the contents of an aggregate object without exposing the representation of its internal details.

6 Code explanation

In Java, Collection, List, Set, Map, etc. all contain iterators. Next, let's take a look at the iterator implementation of List in Java.

6.1 Iterator interface Iterator

public interface Iterator<E> {
    //获取下一个元素,第一次调用给出第一项,第二次给出第二项,。。。
     E next();
     //是否存在下一个,存在true,不存在false
     boolean hasNext();
     //从底层集合中删除迭代器返回的最后一个元素,就是next()返回的集合中的元素
     default void remove() {
        throw new UnsupportedOperationException("remove");
     }
     //对每个剩余的元素进行一定的操作,Consumer是函数式接口
     default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }

}

The default function in the interface is a newly introduced feature of Java8. The default modified method can only be used in the interface. The method marked by default in the interface is a common method, and the method body can be written directly.

6.2 Implementation of Iterator in ArrayList

Get an Iterator instance

public Iterator<E> iterator() {
        return new Itr();
    }

private class Itr

private class Itr implements Iterator<E> {
        //下一个元素返回的索引
        int cursor;
        //最后一个元素的索引,没有为-1,获取实例Iterator时,为-1
        int lastRet = -1;
        // expectedModCount 预期集合元素被修改次数 modCount 集合被修改的数量
        int expectedModCount = modCount;

        Itr() {}

        /**
         * 判断元素
         * @return 是否还有元素
         */
        public boolean hasNext() {
            return cursor != size;
        }

        //返回下一个元素
        @SuppressWarnings("unchecked")
        public E next() {
            // 关键一步,调用checkForComodification()会去校验expectedModCount 和modCount是否相等,不等抛异常
            //集合元素被修改一次,modCount自加一次
            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;
            return (E) elementData[lastRet = i];
        }

        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);
                cursor = lastRet;
                lastRet = -1;
                //修改 让预期修改次数与实际修改次数相等
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

ArrayList.this.remove(lastRet)

 public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work

        return oldValue;
    }

6.3 Main function test

public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i + 1);
        }
        Iterator iterator = list.iterator();
        while(iterator.hasNext()){
            System.out.println(iterator.next());
            iterator.remove();
        }
    }

6.4 Debug test results

1. After adding elements to the list, an Iterator instance is obtained. Pay attention to the value of cursor and the value of lastRet.

2. After deleting an element, iterator changes.

3. Final output result

6.5 Summary

This section analyzes the implementation of Iterator in ArrayList to illustrate the design and application of the iterator pattern. This is in line with our actual programming habits. In daily development, we hardly write iterators ourselves. Unless you need to customize an iterator corresponding to a data structure implemented by yourself, the API provided by the open source framework is completely sufficient.

7 Quotes

1. "Dahua Design Patterns"

2. Iterator mode (detailed version)

3. Argument that the collection cannot be modified when using an iterator to traverse the collection

8 Source code

Because the explanation is the source code of ArrayList in Java, there is no source code for this design pattern.

Guess you like

Origin blog.csdn.net/honor_zhang/article/details/120966930