This repository is an attempt to implement the iterator pattern code piece located at Design Patters: Elements of Reusable Object-Oriented Software1 (page 66/358).
The implementation is an interface-based contract that offers useful
ways to iterate over Objects that holds a collection of elements
(List, Set, Map, ...). I chose a binary tree 'cause it
provides three common ways to iterate over it's data (traversal
ordering):
image 1: orders repr
| Pre-Order traversal | when we look to the current node, then, the left and right one, respectively |
|---|---|
| Post-Order traversal | when we look to the left node, then, the right and current one, respectively |
| In-Order traversal | when we look to the left node, then, the current and right one, respectively |
Tip
Keep in mind that all ordering traversals works in a recursive way, so:
Pre-Orderwill start the iteration from the current node and then, navigates left to right in our tree (trees first, leafs last)(unsorted)Post-Orderwill start the iteration from the left node and then, goes to right in our tree, but leaving the current node for last (leafs first, trees last)(unsorted)In-Orderwill start from the "most-left" subtree and navigate to the "most-right" by passing through the current node (left leaf - current node - right leaf schema)(sorted)
To clone the project you can:
# clone using git
git clone https://github.com/nasccped/DP-iterator-example
# remove git folder (works for bash based terminals, only)
rm -rf DP-iterator-example/.git/
# goto dir
cd DP-iterator-exampleSince this project was built under the kojamp2 project manager, you can use it to handle the project, or:
- compile using a bash terminal (Linux):
javac $(find src -type f -name **.java) -d out- compile using a powershell terminal (Windows):
javac (Get-ChildItem -Recurse -Path src -Filter *.java | ForEach-Object { $_.FullName }) -d outFinally, you can execute the program by running:
java --class-path out IteratorExampleImportant
Make sure to uncomment the code that "runs" the stuff:
public static void main(String[] args) {
// a lot of code...
String sourcePath = "src/IteratorExample.java";
Printer.echoln(
"All the important calls are at `"
+ yellow(sourcePath) + "`'s main function.\n"
+ "You should uncomment the code to see the impl working...\n");
// uncomment the following code (~line 70)
// inOrderExample();
// preOrderExample();
// postOrderExample();
}If you already have experience with Java development, you probably
know that the JDK provides an
Iterator
utility. So, why did I create a new interface instead of using the
built in one?
Even though the Java's iterator provide useful features, my interface is based on this C++ code sample (from the Design Patterns book):
Glyph* g;
Iterator<Glyph*>* i = g->CreateIterator();
for (i->First(); !i->IsDone(); i->Next()) {
Glyph* child = i->CurrentItem();
// do something with current child
}Key points (apparently):
i->First()initializes the iteratori->IsDone()checks if the iterator is donei->Next()moves to the next item in our iteratori->CurrentItem()returns the current item in our iterator
So, I've decided to use this features to build my own iterator
interface
(src/iterators/DPIterator.java).
Note
I mostly mention the interface, but abstract class concept is also
used under this project. Consider "interface" not as the Java's
feature, but the concept itself. (interface as Java's both
interface and abstract class).
The iterator object works like the
previous code sample. By using a queue
data structure (LinkedList in this scenario), we can
initialize and gradually removes the items to "go next" with
our iterator.
Since the First, IsDone, Next and CurrentItem will works all
the same independently of the traversal kind, we can build an
abstract class3 to implement all the
default methods and avoid code repetition:
/**
* Interface file tree:
* interface-package β
* β Interface.java
* β AbstractClass.java
* β concrete classes ...
*/
// AbstractClass.java
abstract class Abstract<Generic> implements Interface<Generic> {
// inner properties...
// interface default implementations...
}Analyzing the code reference, we can define a BinaryTree node as
origin (init the iterator process from this object), a Queue
that holds the elements as "next" and a Generic type variable
that holds the current object:
abstract class Abstract<Generic> implements Interface<Generic> {
private BinaryNode<Generic> origin;
protected Queue<Generic> queue;
private Generic current;
}Here, we can implement all the methods that will works the same for any tree traversal!
Since our iterator is a queue based object, we can initialize it by populating our queue and moving to the next item:
@Override
public void first() {
if (!queue.isEmpty()) queue.clear();
populateQueue(origin);
next();
}Note
Binary Tree is a recursive data structure, so, we need to
populate our queue recursively. Keep in mind that the populate
method must differ from a traversal kind to another (the
populateQueue method should be an
abstract method).
Points the current variable to the next object in our iterator queue:
@Override
public void next() { current = queue.poll(); }I'm using poll instead of remove 'cause the current variable
should be null to indicates the iterator ends.
Returns the iteration is done by checking the queue (no Object
remaining) and the current variable (null obj):
@Override
public boolean isDone() { return queue.isEmpty() && current == null; }Returns the Object being held by the current variable:
@Override
public Generic getCurrentItem() { return current; }Finally, the queue populate method is different for each traversal
kind, so it should be implemented only in the
concrete class4.
Let's take the In-Order traversal as example:
- base case: is where our queue pushing stops. We'll never push a
nullobject to our queue. That's our base case! - node order:
in-ordertraversal works "most left to most right" (sorted). We should push the left node first, then, the current node, and finally, the right one:
public class InOrderIter<T extends Comparable<T>>
extends Abstract<T> {
// receives a binary tree node (allow recursion)
@Override
protected void populateQueue(BinaryNode<T> node) {
// if non valid object, stops recursion
if (node == null) return;
populateQueue(node.getLeftNode()); // go left node recursion
queue.add(node.getCurrent()); // insert current node value
populateQueue(node.getRightNode()); // go right node recursion
}
}The populateQueue implement will be different for each child
class5. They all works the same but differ in object
pushing order!
Footnotes
-
Design Patters: Elements of Reusable Object-Oriented Software is a software engineering book that describes software design patterns. You can find it at amazon website. β©
-
Kojamp is a Java and Kotlin project manager I built. You can find more info on it's official repository β©
-
abstract classserves as a blueprint for other classes and cannot be instantiated directly, meaning you cannot create objects of an abstract class. More info at Oracle website. β© -
Concrete class it's a class that is fully implemented and can be instantiated, unlike an abstract class or interface. β©
-
Child class, also known as a subclass or derived class, is a new class that inherits properties and methods from an existing class, called the parent class (or base class). β©
