Main contents of this paper:

  • Introduces the iterator pattern

  • A typical application of the source code analysis iterator pattern

    • Iterator pattern in Java collections

    • Iterator pattern in Mybatis

Iterator pattern

Iterator Pattern: Provides a way to access an aggregate object without exposing the internal representation of the object, alias Cursor. The iterator pattern is an object behavior pattern.

role

Iterator: Defines interfaces for accessing and iterating over elements and declares methods for iterating over data elements, for example: The first() method to get the first element, the next() method to access the next element, the hasNext() method to determine if there is a next element, the currentItem() method to get the current element, and so on, will be implemented in concrete iterators.

ConcreteIterator: implements an abstract iterator interface that iterates through an aggregate object and records the current location in the aggregate object in a ConcreteIterator via a cursor, which is usually a non-negative integer representing the location.

Aggregate (Abstract Aggregate class) : This is used to store and manage element objects. A createIterator() method is declared to create an iterator object that acts as an abstract iterator factory.

ConcreteAggregate: This implements the createIterator() method declared in the abstract aggregate class, which returns an instance of ConcreteIterator corresponding to the ConcreteAggregate class.

In the iterator pattern, an external iterator is provided to access and traverse an aggregate object. The iterator defines an interface to access the aggregate element and can keep track of the currently iterated elements to see which elements have been traversed and which have not. The introduction of iterators will make it easier to operate on a complex aggregate object.

The factory method pattern is applied to the iterator pattern, where abstract iterators correspond to abstract product roles, concrete iterators to concrete product roles, abstract aggregate classes to abstract factory roles, and concrete aggregate classes to concrete factory roles.

The sample

Let’s implement an example of a student counting

Define a student class with a count() method

@Getter@Setter@ToStringpublic class Student { private String name; private Integer number; public Student(String name, Integer number) { this.name = name; this.number = number; } public void count() {system.out.println (String. Format (" I am %d %s", this.number, this.name));} public void count() {system.out.println (String. }}Copy the code

Define class interfaces and class classes

public interface StudentAggregate { void addStudent(Student student); void removeStudent(Student student); StudentIterator getStudentIterator(); }public class StudentAggregateImpl implements StudentAggregate { private List<Student> list; Public StudentAggregateImpl() {this.list = new ArrayList<Student>(); } @Override public void addStudent(Student student) { this.list.add(student); } @Override public void removeStudent(Student student) { this.list.remove(student); } @Override public StudentIterator getStudentIterator() { return new StudentIteratorImpl(list); }}Copy the code

Define the iterator interface and implement the iterator

public interface StudentIterator { boolean hashNext(); Student next(); }public class StudentIteratorImpl implements StudentIterator{ private List<Student> list; private int position = 0; private Student currentStudent; public StudentIteratorImpl(List<Student> list) { this.list = list; } @Override public boolean hashNext() { return position < list.size(); } @Override public Student next() { currentStudent = list.get(position); position ++; return currentStudent; }}Copy the code

Test, count off

public class Test { public static void main(String[] args) { StudentAggregate classOne = new StudentAggregateImpl(); Classone. addStudent(new Student(" 三", 1)); classone. addStudent(new Student(" 三", 1)); Classone. addStudent(new Student(" 三 ", 2)); Classone. addStudent(new Student(" 五", 3)); Classone. addStudent(new Student(" zhao liu ", 4)); / / traverse, count off StudentIterator iterator = classOne. GetStudentIterator (); while (iterator.hashNext()){ Student student = iterator.next(); student.count(); }}}Copy the code

The output

I'm Zhang SAN 1. I'm Li Si 2. I'm Wang Wu 3. I'm Zhao Liu 4Copy the code

The iterator pattern class diagram is shown below

Example. Iterator class diagram

Iterator pattern summary

The main advantages of the iterator pattern are as follows:

  • It supports traversal of an aggregate object in different ways, and multiple traversals can be defined on the same aggregate object. In iterator mode, we can change the traversal method by replacing the original iterator with a different iterator. We can also define a subclass of iterators to support the new traversal method.

  • Iterators simplify aggregate classes. Due to the introduction of iterator, there is no need to provide data traversal and other methods in the original aggregation object, which can simplify the design of aggregation class.

  • In the iterator pattern, due to the introduction of an abstraction layer, it is easy to add new aggregate classes and iterator classes without modifying the original code, which meets the requirements of the “open closed principle”.

The main disadvantages of the iterator pattern are as follows:

  • Since the iterator pattern separates the responsibility of storing data and traversing data, adding new aggregation classes needs to correspond to adding new iterator classes, and the number of classes increases in pairs, which increases the complexity of the system to some extent.

  • Abstract Iterator is difficult to design, and the future expansion of the system needs to be fully taken into account. For example, the built-in Iterator in JDK cannot realize reverse traversal. If reverse traversal is needed, it can only be realized through its subclass ListIterator. ListIterator iterators cannot be used to manipulate aggregate objects of type Set. When customizing iterators, it is not easy to create a fully considered abstract iterator.

Applicable scenarios:

  • Access the content of an aggregate object without exposing its internal representation. The access to the aggregate object is separated from the storage of the internal data so that the aggregate object can be accessed without knowing the internal implementation details.

  • You need to provide multiple traversals for an aggregate object.

  • Provides a unified interface for traversing different aggregation structures, provides different traversals for different aggregation structures in the implementation class of this interface, and the client can operate on this interface consistently.

A typical application of the source code analysis iterator pattern

Iterator pattern in Java collections

Look at the Java. Util. The ArrayList class

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { transient Object[] elementData; // non-private to simplify nested class access private int size; public E get(int index) { rangeCheck(index); return elementData(index); } public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } public ListIterator<E> listIterator() { return new ListItr(0); } public ListIterator<E> listIterator(int index) { if (index < 0 || index > size) throw new IndexOutOfBoundsException("Index: "+index); return new ListItr(index); } public Iterator<E> iterator() { return new Itr(); } private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = -1; // index of last element returned; -1 if no such int expectedModCount = modCount; public boolean hasNext() { return cursor ! = size; } public E next() { //... } public E next() { //... } public void remove() { //... } / /... } private class ListItr extends Itr implements ListIterator<E> { public boolean hasPrevious() { return cursor ! = 0; } public int nextIndex() { return cursor; } public int previousIndex() { return cursor - 1; } public E previous() { //... } public void set(E e) { //... } public void add(E e) { //... } / /... }Copy the code

Iterators Itr and ListItr implement Iterator and ListIterator interfaces respectively.

The first, of course, is easy to see. It differs from our example iterator in that this is an inner class that uses a list of data directly from an ArrayList; The second Iterator is for the first time. What is the difference between ListIterator and Iterator?

First look at ListIterator source

public interface ListIterator<E> extends Iterator<E> { boolean hasNext(); E next(); boolean hasPrevious(); // Returns whether the set associated with this iterator has the previous element E previous(); // Return the last element of the iterator int nextIndex(); Int previousIndex(); // Return the index of the element after the desired position of ListIterator. Void remove(); // Return the index of the element before ListIterator. void set(E var1); // Change the last element returned by next() or previous() from the list to the specified element e void add(e var1); }Copy the code

Then comes the source code for Iterator

public interface Iterator<E> { boolean hasNext(); E next(); default void remove() { throw new UnsupportedOperationException("remove"); } // Note: JAVA8 allows interface method definitions to be implemented default void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); while (hasNext()) action.accept(next()); }}Copy the code

ListIterator is a more powerful Iterator that inherits from the Iterator interface and can only be used for all types of List access. You can call the listIterator() method to produce a listIterator that points to the beginning of the List, or you can call the listIterator(n) method to create a listIterator that starts with an element indexed n in the List.

The main differences between Iterator and ListIterator are summarized as follows:

  • ListIterator has an add() method that adds objects to a List, whereas Iterator does not

  • ListIterator and Iterator both have hasNext() and next() methods that iterate backwards, but ListIterator has hasPrevious() and previous() methods, Reverse (sequential forward) traversal is possible. Iterator does not.

  • ListIterator can locate the current index position, nextIndex() and previousIndex() can do this. Iterator does not do this.

  • Both delete objects, but ListIterator can modify objects, and the set() method can. The Iierator can only be traversed, but cannot be modified.

Hit a Demo of Iterator to explore

public class Test3 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); List. The add (" zhang "); List. The add (" li si "); List. The add (" detective "); List. The add (" Daisy "); Iterator<String> iterator = list.iterator(); String first = iterator.next(); System.out.println("first: " + first); System.out.println("-----------next-------------"); while (iterator.hasNext()){ System.out.println(iterator.next()); } iterator.remove(); System.out.println("-----------list-------------"); for (String name: list){ System.out.println(name); }}}Copy the code

The output

First: zhang -- -- -- -- -- -- -- -- -- -- -- next -- -- -- -- -- -- -- -- -- -- -- -- -- li si fifty and Daisy -- -- -- -- -- -- -- -- -- -- -- the list -- -- -- -- -- -- -- -- -- -- -- -- -- Tom, dick and harry fiftyCopy the code

Iterator.remove() will remove the data from the original List object

Click on a ListIterator Demo to explore

public class Test2 { public static void main(String[] args) { List<String> list = new ArrayList<String>(); List. The add (" zhang "); List. The add (" li si "); List. The add (" detective "); List. The add (" Daisy "); ListIterator<String> listIterator = list.listIterator(); String first = listIterator.next(); ListIterator. Set (" Ming "); System.out.println("first: " + first); System.out.println("-----------next-------------"); ListIterator. Add (" Ming "); while (listIterator.hasNext()){ System.out.println(listIterator.nextIndex() + ": " + listIterator.next()); } listIterator.remove(); System.out.println("------------previous------------"); while (listIterator.hasPrevious()){ System.out.println(listIterator.previousIndex() + ": " + listIterator.previous()); } System.out.println("-----------list-------------"); for (String name: list){ System.out.println(name); }}}Copy the code

The results are as follows

First: zhang -- -- -- -- -- -- -- -- -- -- -- next -- -- -- -- -- -- -- -- -- -- -- -- -- 2: li si 3: fifty and 4: zhao six -- -- -- -- -- -- -- -- -- -- -- -- previous -- -- -- -- -- -- -- -- -- -- -- -- 3: fifty and 2: li si 1: daming 0: Xiao Ming -----------list------------- Xiao Ming Da Ming Li Si Wang WuCopy the code

ListIterator’s add, set, and remove methods change the List object directly and iterate through previous in reverse

Iterator pattern in Mybatis

When the query database returns a large number of data items, the Cursor can be used. The iterator can be lazy to load data, avoiding memory collapse caused by loading all data at one time. Mybatis provides a default implementation class DefaultCursor for Cursor interface, the code is as follows

public interface Cursor<T> extends Closeable, Iterable<T> { boolean isOpen(); boolean isConsumed(); int getCurrentIndex(); }public class DefaultCursor<T> implements Cursor<T> { private final DefaultResultSetHandler resultSetHandler; private final ResultMap resultMap; private final ResultSetWrapper rsw; private final RowBounds rowBounds; private final ObjectWrapperResultHandler<T> objectWrapperResultHandler = new ObjectWrapperResultHandler<T>(); Private final CursorIterator CursorIterator = new CursorIterator(); protected T fetchNextUsingRowBound() { T result = fetchNextObjectFromDatabase(); while (result ! = null && indexWithRowBound < rowBounds.getOffset()) { result = fetchNextObjectFromDatabase(); } return result; } @Override public Iterator<T> iterator() { if (iteratorRetrieved) { throw new IllegalStateException("Cannot open more than one iterator on a Cursor"); } iteratorRetrieved = true; return cursorIterator; } private class CursorIterator implements Iterator<T> { T object; int iteratorIndex = -1; @Override public boolean hasNext() { if (object == null) { object = fetchNextUsingRowBound(); } return object ! = null; } @Override public T next() { T next = object; if (next == null) { next = fetchNextUsingRowBound(); } if (next ! = null) { object = null; iteratorIndex++; return next; } throw new NoSuchElementException(); } @Override public void remove() { throw new UnsupportedOperationException("Cannot remove element from Cursor"); }} / /... }Copy the code

The CursorIterator, CursorIterator, implements the java.util.Iterator interface, where the Iterator pattern is almost identical to that of the Iterator in ArrayList

Reference: liu wei: design patterns Java version earnestly longed for Java class network design patterns the Debug mode + memory analysis in Java collection about the Iterator and a ListIterator explanation

Click here to visit my personal blog: http://laijianfeng.org

Follow the wechat official account of Xiao Shuang Feng

Recommended reading

Design patterns and typical application of design patterns | | simple factory pattern factory method pattern and a typical application of design patterns | the abstract factory pattern and a typical application of design pattern model and typical application design | | builders prototype model and typical application design model and typical application design | | appearance Decorator pattern and a typical application of design pattern model and typical application design | | adapter the flyweight pattern and a typical application of design patterns and typical application of design patterns | | combination mode template method pattern and typical applications