© Vaskaran Sarcar 2019
Vaskaran SarcarJava Design Patternshttps://doi.org/10.1007/978-1-4842-4078-6_18

18. Iterator Pattern

Vaskaran Sarcar1 
(1)
Bangalore, Karnataka, India
 

This chapter covers the iterator pattern.

GoF Definition

Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation.

Concept

Using iterators, a client object can traverse a container (or a collection of objects) to access its elements without knowing how these data are stored internally. The concept is very useful when you need to traverse different kinds of collection objects in a standard and uniform way. The following are some important points about this pattern.
  • It is often used to traverse the nodes of a tree-like structure. So, in many scenarios, you may notice the use of iterator patterns with composite patterns.

  • The role of an iterator is not limited to traversing. This role can vary to support various requirements.

  • Clients cannot see the actual traversal mechanism. A client program only uses the iterator methods that are public in nature.

  • Figure 18-1 shows a sample diagram for an iterator pattern.

../images/395506_2_En_18_Chapter/395506_2_En_18_Fig1_HTML.jpg
Figure 18-1

A sample diagram for an iterator pattern

The participants are as follows:
  • Iterator: An interface to access or traverse elements.

  • ConcreteIterator: Implements the Iterator interface methods. It can also keep track of the current position in the traversal of the aggregate.

  • Aggregate: Defines an interface that can create an Iterator object.

  • ConcreteAggregate: Implements the Aggregate interface. It returns an instance of ConcreteIterator.

Real-World Example

Suppose there are two companies: company A and company B. Company A stores its employee records (i.e., name, address, salary details, etc.) in a linked list data structure. Company B stores its employee data in an array data structure. One day, the two companies decide to merge to form a large organization. The iterator pattern is handy in such a situation because the developers do not want to write code from scratch. They can create a common interface so that they can access the data for both companies and invoke the methods in a uniform way.

Consider another example. Suppose that your company has decided to promote some employees based on their performances. So, all the managers get together and set a common criterion for promotion. Then they iterate over the past records of each employee to mark potential candidates for promotion.

Lastly, when you store songs in your preferred audio devices— an MP3 player or your mobile devices, for example, you can iterate over them through various button press or swipe movements. The basic idea is to provide you some mechanism to smoothly iterate over your list.

Computer-World Example

Similarly, let’s assume that, a college arts department is using an array data structure to maintain its students’ records. The science department is using a linked list data structure to keep their students’ records. The administrative department does not care about the different data structures, they are simply interested in getting the data from each of the departments and they want to access the data in a universal way.

Note

The iterator classes in Java’s collection framework are iterator examples. When you use the interfaces like java.util.Iterator or java.util.Enumeration , you basically use this pattern. The java.util.Scanner class also follows this pattern. If you are familiar with C#, you may use C#’s own iterators that were introduced in Visual Studio 2005.The foreach statement is frequently used in this context.

Illustration

In this chapter, there are three different implementations of the iterator pattern. I’ll start with an example that follows the core theory of this pattern. In the next example, I’ll modify the example using Java’s built-in support of the iterator pattern. In the third and final example, you use this pattern with a different data structure. In the first two examples, I’ll simply use “String” data types but in the final example, I’ll use a complex data type.

Before you start, I suggest that you note the structure in the Package Explorer view for your immediate reference.

In the first implementation, let’s assume that in a particular college, an arts department student needs to study four papers (or subjects)—English, history, geography, and psychology. The details of these papers are stored in an array data structure. And your job is to print the curriculum using an iterator.

Let’s assume that your iterator currently supports four basic methods: first(), next(), currentItem(), and hasNext().
  • The first() method resets the pointer to the first element before you start traversing a data structure.

  • The next() method returns the next element in the container.

  • The currentItem() method returns the current element of the container that the iterator is pointing at a particular point of time.

  • The hasNext() validates whether any next element is available for further processing. So, it helps you determine whether you have reached the end of your container.

Class Diagram

Figure 18-2 shows the class diagram.
../images/395506_2_En_18_Chapter/395506_2_En_18_Fig2_HTML.jpg
Figure 18-2

Class diagram

Note

Like many of the previous examples in this book, to present a clean class diagram, I have shown only client code dependencies. For any ObjectAid class diagrams shown in the Eclipse editor, you can always see other dependencies by selecting an element in the diagram, right-clicking it, and selecting Add ➤ Dependencies.

Package Explorer View

Figure 18-3 shows the high-level structure of the program.
../images/395506_2_En_18_Chapter/395506_2_En_18_Fig3_HTML.jpg
Figure 18-3

Package Explorer view

First Implementation

Here’s the first implementation.
package jdp2e.iterator.demo;
interface Subjects
{
    Iterator createIterator();
}
class Arts implements Subjects
{
    private String[] papers;
    public Arts()
    {
        papers = new String[] { "English","History", "Geography","Psychology" };
    }
    public Iterator createIterator()
    {
        return new ArtsIterator(papers);
    }
}
interface Iterator
{
    void first();//Reset to first element
    String next();//To get the next element
    String currentItem();//To retrieve the current element
    boolean hasNext();//To check whether there is any next element or not.
}
class ArtsIterator implements Iterator
{
    private String[] papers;
    private int position;
    public ArtsIterator(String[] papers)
    {
        this.papers = papers;
        position = 0;
    }
    @Override
    public void first()
    {
        position = 0;
    }
    @Override
    public String next()
    {
        //System.out.println("Currently pointing to: "+ this.currentItem()) ;
        return papers[position++];
    }
    @Override
    public String currentItem()
    {
        return papers[position];
    }
    @Override
    public boolean hasNext()
    {
        if(position >= papers.length)
            return false;
        return true;
    }
}
public class IteratorPatternExample {
    public static void main(String[] args) {
        System.out.println("***Iterator Pattern Demo***");
        Subjects artsSubjects = new Arts();
        Iterator iteratorForArts = artsSubjects.createIterator();
        System.out.println(" Arts subjects are as follows:");
        while (iteratorForArts.hasNext())
        {
            System.out.println(iteratorForArts.next());
        }
        //Moving back to first element
        iteratorForArts.first();
        System.out.println(" Currently pointing back to: "+ iteratorForArts.currentItem());
    }
}

Output

Here’s the output.
***Iterator Pattern Demo***
 Arts subjects are as follows:
English
History
Geography
Psychology
 Currently pointing back to: English

Note

If you want to see the current element that the iterator is pointing to, you can uncomment the line in the next() method: // System.out.println("Currently pointing to: "+ this.currentItem());

Now let’s modify the previous implementation using Java’s built-in Iterator interface.

Key Characteristics of the Second Implementation

I used Java’s built-in support for the iterator pattern. Note the inclusion of the following line at the beginning of the program.
                        import java.util.Iterator;

If you open the source code, you see that this interface has three methods: hasNext(), next(), and remove(). But the remove() method has a default implementation already. So, in the following example, I needed to override the hasNext() and next() methods only.

Here you are using the Java’s Iterator interface, so there is no need to define your own Iterator interface.

In this modified implementation, key changes are shown in bold.

Second Implementation

Here’s the second implementation.
package jdp2e.iterator.modified.demo;
import java.util.Iterator;
interface Subjects
{
    //Iterator CreateIterator();
    ArtsIterator createIterator();
}
class Arts implements Subjects
{
    private String[] papers;
    public Arts()
    {
        papers = new String[] { "English","History", "Geography","Psychology" };
    }
    //public Iterator CreateIterator()
    public ArtsIterator createIterator()
    {
        return new ArtsIterator(papers);
    }
}
class ArtsIterator implements Iterator<String>
{
    private String[] papers;
    private int position;
    public ArtsIterator(String[] papers)
    {
        this.papers = papers;
        position = 0;
    }
    public void first()
    {
        position = 0;
    }
    public String currentItem()
    {
        return papers[position];
    }
    @Override
    public boolean hasNext()
    {
        if(position >= papers.length)
            return false;
        return true;
    }
    @Override
    public String next()
    {
        return papers[position++];
    }
}
public class ModifiedIteratorPatternExample {
    public static void main(String[] args) {
        System.out.println("***Modified Iterator Pattern Demo.***");
        Subjects artsSubjects = new Arts();
        //Iterator IteratorForArts = artsSubjects.createIterator();
        ArtsIterator iteratorForArts = artsSubjects.createIterator();
        System.out.println(" Arts subjects are as follows:");
        while (iteratorForArts.hasNext())
        {
            System.out.println(iteratorForArts.next());
        }
        //Moving back to first element
        iteratorForArts.first();
        System.out.println("Currently pointing to: "+ iteratorForArts.currentItem());
    }
}

Output

Here’s the modified output.
***Modified Iterator Pattern Demo.***
Arts subjects are as follows:
English
History
Geography
Psychology
Currently pointing to: English

Q&A Session

  1. 1.
    What is the use of an iterator pattern?
    • You can traverse an object structure without knowing its internal details. As a result, if you have a collection of different subcollections (e.g., your container is mixed up with arrays, lists, or linked lists, etc.), you can still traverse the overall collection and deal with the elements in a universal way without knowing the internal details or differences among them.

    • You can traverse a collection in different ways. You can also provide an implementation that supports multiple traversals simultaneously.

     
  2. 2.

    What are the key challenges associated with this pattern?

    Ideally, during a traversal/iteration process, you should not perform any accidental modification to the core architecture.

     
  3. 3.

    But to deal with the challenge mentioned earlier, you could make a backup and then proceed. Is this correct?

    Making a backup and reexamining later is a costly operation.

     
  4. 4.

    Throughout the discussion, you have talked about collections. What is a collection?

    It is a group of individual objects that are presented in a single unit. You may often see the use of the interfaces like java.util.Collection, java.util.Map, and so forth, in Java programs. These are some common interfaces for Java’s collection classes, which were introduced in JDK 1.2.

    Prior to collections, you had choices like arrays, vectors, and so forth, to store or manipulate a group of objects. But these classes did not have a common interface; the way you needed to access elements in an array were quite different from the way you needed to access the elements of a vector. That is why it was difficult to write a common algorithm to access different elements from these different implementations. Also, many of these methods were final, so you could not extend them.

    The collection framework was introduced to address these kinds of difficulties. At the same time, they provided high-performance implementations to make a programmer’s life easier.

     
  5. 5.

    In the modified implementation, why am I not seeing the @Override annotation for the first() and currentItem() methods?

    These two methods are not present in the java.util.Iterator interface. The built-in Iterator interface has the hasNext() and next() methods. So, I used the @Override annotation for these methods. There is another method, remove(), in this interface. It has a default implementation. Since I have not used it, I did not need to modify this method.

     
  6. 6.

    In these implementations, I am seeing that you are only using strings of arrays to store and manipulate data. Can you show an iterator pattern implementation that uses a relatively complex data type and a different data structure?

    To make these examples simple and straightforward, I only used strings and an array data structure. You can always choose your preferred data structure and apply the same process when you consider a complex data type. For example, consider the following illustration (third implementation) with these key characteristics.
    • Here I am using a relatively complex data type, Employee. Each employee object has three things: a name, an identification number (id), and the salary.

    • Instead of an array, I used a different data structure, LinkedList, in the following implementation. So, I need to include the following line in this implementation.

      import java.util.LinkedList;

    • I have followed the same approach that I used in the previous example.

     

Third Implementation

Here’s the third implementation.
package jdp2e.iterator.questions_answers;
import java.util.Iterator;
import java.util.LinkedList;
class Employee
{
    private String name;
    private int id;
    private double salary;
    public Employee(String name, int id, double salary )
    {
        this.name=name;
        this.id=id;
        this.salary=salary;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public double getSalary() {
        return salary;
    }
    public void setSalary(double salary) {
        this.salary = salary;
    }
    @Override
    public String toString(){
        return "Employee Name: "+this.getName()+", ID: "+this.getId()+ " and salary: "+this.getSalary()+"$";
    }
}
interface DataBase
{
    EmployeeIterator createIterator();
}
class EmployeeDatabase implements DataBase
{
    private LinkedList<Employee> employeeList;
    public EmployeeDatabase()
    {
        employeeList = new LinkedList<Employee>();
        employeeList.add(new Employee("Ron",1, 1000.25));
        employeeList.add(new Employee("Jack",2, 2000.5));
        employeeList.add(new Employee("Ambrose",3, 3000.75));
        employeeList.add(new Employee("Jian",4, 2550.0));
        employeeList.add(new Employee("Alex",5, 753.83));
    }
    public EmployeeIterator createIterator()
    {
        return new EmployeeIterator(employeeList);
    }
}
class EmployeeIterator implements Iterator<Employee>
{
    private LinkedList<Employee> employeeList;
    private int position;
    public EmployeeIterator(LinkedList<Employee> employeeList)
    {
        this.employeeList= employeeList;
        position = 0;
    }
    //@Override
    public void first()
    {
        position = 0;
    }
    //@Override
    public Employee currentItem()
    {
        return employeeList.get(position);
    }
    @Override
    public Employee next()
    {
        return employeeList.get(position++);
    }
    @Override
    public boolean hasNext() {
        if(position >= employeeList.size())
            return false;
        return true;
    }
}
public class ModifiedIteratorPatternExample2 {
    public static void main(String[] args) {
        System.out.println("***Modified Iterator Pattern Demo.Example-2.***");
        DataBase employeesList = new EmployeeDatabase();
        EmployeeIterator iteratorForEmployee = employeesList.createIterator();
        System.out.println(" -----Employee details are as follows----- ");
        while (iteratorForEmployee.hasNext())
        {
            System.out.println(iteratorForEmployee.next());
        }
    }
}

Output

Here’s the output from the third implementation.
***Modified Iterator Pattern Demo.Example-2.***
 -----Employee details are as follows-----
Employee Name: Ron, ID: 1 and salary: 1000.25$
Employee Name: Jack, ID: 2 and salary: 2000.5$
Employee Name: Ambrose, ID: 3 and salary: 3000.75$
Employee Name: Jian, ID: 4 and salary: 2550.0$
Employee Name: Alex, ID: 5 and salary: 753.83$

Note

You may use two or more different data structures in an implementation to demonstrate the power of this pattern. You have seen that across these different implementations, I have used the first(), next(), hasNext(), and currentItem() methods with different implementations that vary due to their internal data structures.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.15.137.59