Iterator Design Pattern
Overview
- Iterator design pattern is a commonly used design pattern in java and .Net programming. This pattern is used to get a way to access the elements of a collection object in sequential manner without any need to know its underlying representation.
- Iterator pattern falls under Behavioral pattern.
- Instead of programmer writing the code for Iterations and looping, java provides inbuilt iterators which gives implementation and programmers use them.
- Iterators in java are used in the collection framework to retrive elements one by one.
Syntax
Iterator iterator = c.iterator();
- Here c is any collection object. iterator is of type Iterator interface and refers to c.
Methods of Iterator Interface in java
- hasNext()
public boolean hasNext() - next()
public Object next(). next() method throws 1 exception. NoSuchElementException β> if no more element is present. - remove()
public void remove(). Remove() method throws 2 exceptions. UnsupportedOperationException β> if remove operation is not supported by this Iterator. IllegalStateException β> if next method has not yet been called or remove method has already been called after the last call to the next method.
Example 1
public class I1Intro {
public static void main(String ...args) {
List<Integer> numbers = List.of(1,2,3,4,5,6);//Arrays.asList()
Integer sum = 0;//Integer.MIN_VALUE;
//Declarative Style === what to do, delegate how to do to Java
int sumTotal = numbers.stream().parallel()
.mapToInt(number -> number)
.sum();
System.out.println(sumTotal);
}
}
Example 2
- Iterating using forLoop with var
public class Iterators { private static List<String> forLoopWithVar(List<String> list, int count) { var ret = new ArrayList<String>(); var input = list; int x = 0; for(var name : list){ var str = name; if(str.length() > 4){ x++; ret.add(str.toUpperCase()); } if(x == count){ break; } } return ret; } - Iterating using For loop
private static List<String> forLoop(List<String> list, int count) { //and then //Imperative style === what to do + how to do + Eager Evaluation int x = 0; List<String> ret = new ArrayList<>(); //send first 2 string with lenght > 4 for (int i = 0; i < list.size(); i++) { String str = list.get(i); if(str.length() > 4){ x++; ret.add(str.toUpperCase()); } if(x == count){ break; } } return ret; } } - Iterating with forLoop using Streams
private static List<String> forLoopStreams(List<String> list, int count) { var ret = new ArrayList<String>(); //Functional Style == declarative style //Works until now, but stooped all of a sudden // Code behaves - erratically with parallel Stream ret = (ArrayList<String>) list.stream().parallel() //.filter(name -> null != name) .filter(Objects::nonNull) .filter(name -> name.length() > 2) .map(nameInLowerCase -> nameInLowerCase.toUpperCase()) .limit(count) //.forEach(name -> ret.add(name));//BAD IDEA with ParallelStream - due to shared mutability - this is impure .collect(Collectors.toList());//Thread safe return ret; }
Implementation Example 3
- Creating an Iterator interface which includes navigation method and a Container interface which returns the iterator.
- Concrete classes implementing the Container interface will be responsible to implement Iterator interface and use it.
- IteratorPatternDemo main class will use NamesRepository, a concrete class implementation to print Names stored as collection in NamesRepository.
Creating an interface Iterator.java, Container.java
public interface Iterator{
public boolean hasNext();
public Object next();
}
Creating concrete class implementing the container interface.
public class Namerepository implements Container{
public String names[] = {"Deepthi", "Nitin", "Keerthi"};
@Override
public Iterator getIterator(){
return new NameIterator();
}
- This class has inner class NameIterator implementing the Iterator interface NameRepository.java
private class NameIterator implements Iterator{
int index = 0;
@Override
public boolean hasNext(){
if(index < names.length){
return true;
}
return false;
}
@Override
public Object next(){
if(this.hasNext()){
return names[index++];
}
return null;
}
}
Using the NameRepository to get iterator and print names IteratorPatternDemo.java
public class IteratorPtterndemo{
public ststic void main(String[] args){
NameRepository namesRepository = new NameRepository();
for(Iterator iterator = namesRepository.getIterator();
iterator.hasNext();
)
{
String name = (String)iter.next();
System.out.println("Name:"+name);
}
}
}
- The output will be: Name:Deepthi, Name:Keerthi, Name:Nitin
Advantages of iterator Design pattern
- It provides a way for us to implement the accessibility of elements without exposing the underlying representation of the container.
- lets you traverse elements of a collection without exposing its underlying representation (list, stack, tree, etc.). Iterator takes care of which data structure should be implemented internally.
- We can use it for any collection class.
- supports both read and remove operations.