2. Iteration Problem
Root
x
y z
public class Node {
private Object data;
private Map<String, Node> children;
}
u
v
3. Recursion
• Need to traverse model
void recursiveX() {
x();
for (final Node child : children.values())
child.recursiveX();
}
• recursiveY()
• recursiveZ()
• ...
4. External Iteration : Iterator
• Any way to implement recursion once and for
all ?
• Iterator pattern (Java >= 1.2 )
• Wikipedia:
” the iterator pattern is a design pattern in
which an iterator is used to traverse a
container and access the container's
elements”
5. Client In Control
Iterator Behaviour
Root
x
y z
u
v
Client
Iterator()
next()
Iterator
6. Iterator Issues
• The iterator instance has state
– Strong references to provider and internal state
• implementation often difficult
• Thread unsafe by design
7. Internal Iteration - Visitor
• The visitor pattern
• Wikipedia:
” the visitor design pattern is a way of
separating an algorithm from an object
structure on which it operates”
8. Visitor Behaviour
Provider in Control
Root
x
y z
u
v
Client
visitor()
Visitor
accept(Visitor)
visit(Node)
visit(Node)
visit(Node)
12. Visitor – Limitations (Lambda)
• Limited access to local scope
– Final and effectively final variables
• Early exit not possible
– break
– return
13. Internal iteration in Java 8 -
Stream
• java.util.Stream
• Not limited to collections
• Many powerful methods
• Potential gains
– Lazy evaluation
– Parallell execution
• The default implementation (StreamSupport) is
based on (spl)iterators
14. LIMITATIONS
callback()
Stream In Control
Stream
Support
(Spl)Iterator
Default Stream
Implementation
Root
x
y z
u
v
stream() Client
next()
22. Summary
• Many advantages to internal iteration
– Control
– Simplified implementation
– Nice abstraction
• Streams are powerful
– Create your own – it is not that hard
23. Thank You for
Your Time!
Martin Skarsaune
Java Developer and Co-Owner
Notas del editor
Welcome everyone to this talk.
My name is Martin Skarsaune.
I work as a Java Developer at Kantega.
Today I am going to talk a bit about iteration in general, in light of the new Stream interface in Java 8.
So, this may be a familiar situation.
Your application has some sort of hierarchical data structure.
We see that each node has a map of children.
At times there is a need to traverse the structure to apply some sort of business logic.
The most intuitive and elegant way to do this is by recursion.
So we create a recursiveX method to apply x to all items,
…and a recursive method to apply Y
…and a recursive method to apply Z
And so on
The question then is, are there any alternatives to write all this repetitive code ?
It would also be nice if the traversal was not so tightly coupled with the internal structure.
Traditionally iterators have been the dominant approach in Java.
So how does iterators work?
The provider hands over an iterator instance to the client.
From that point on the client drives the iteration.
The iterator must have sufficient state and knowledge to know its position and how to proceed to the next item.
There are some inherent drawbacks with the iterator pattern.
The iterator instance has state and direct references back to the provider and its internal state.
Implementation is often difficult, complex code is more error prone.
For me this is the most important drawback.
You can of course cheat, as I have done many times. Write a recursive method to return a collection of items and pass an iterator to that collection instead.
It may be an ok solution for 100 items, but less so if there are a million.
The iterator is limited to sequential execution
So what alternatives do we have? Another design pattern dealing with iteration is the visitor pattern.
Now how does the visitor pattern work compared to the iterator pattern ?
Here we see that the provider drives the iteration with callbacks back to the client.
So, how could we use a visitor in our example.
First we define a Visitor interface. We see that it only contains one abstract method, and can therefore be used as a functional interface in Java 8.
Then we look at the specific recursive method we created initially.
How can we generalize it to accommodate the Visitor?
If we make a slight modification to our recursive method.
We add the visitor as an argument and call the callback method of the visitor instead of calling x
Now we can easily reuse this method with a lambda argument to reach all elements.
To call x ….
Or y ….
Or z …
So, this seems to be a much more elegant solution to iteration, but what are the drawbacks ?
Since the iteration body is implemented as a lambda, there are certain restrictions.
You do not have full access to the enclosing method scope, only final and effectively final variables.
The iteration will always go through all the items, it is not possible to abort.
So now we have a nice general purpose iteration mechanism.
Is there anything more we can achieve?
Java 8 introduces a powerful new Stream concept.
A stream is as the name implies, a stream of objects.
The stream concept is not limited to collections, this is in my eyes a good design decision as stream is a broader concept and the collection API is bloated enough as it is.
Streams have many powerful methods, and open a lot of possibilities as the iteration is not controlled by the client.
For instance paralell execution and lazily evaluated pipes of streams.
The default implementation in Java is called StreamSupport.
One disappointing thing is that it is based on iterators.
So what now if we want to add stream support to our datastructure with StreamSupport?
In my opinion we get the worst of both worlds.
The provider gets the burden of implementing an iterator, while the client is limited by the restrictions of internal iteration.
What I would really like is to get rid of the dependency on iterator.
So in what way can we extend our visitor implementation to support stream ?
First we generalize our visitor.
Then we introduce a new interface to represent a structure that will accept a visitor
Then we modify the accept method to accept the generic visitor and allow Node to implement the Visitable interface.
But how does this bring us any closer to support for streams.
Instead of Javas built in StreamSupport we create a new Stream implementation called VisitorStream based on a Visitable.
The node class may easily now offer a VisitorStream based stream to clients.
I have organized most of the metods into categories.
Lets see how we intend to support each of them.
First of all ,we do not want to support iterators.
1. Then we look at the very basics.
Streams are about iteration.
We see that the forEach method is easily implemented in a functional manner where one function wraps another.
2. Output is trivial, we simply put the results in a collection
3. Reduction methods are used to combine all the items of a stream into one result.
The implementation is also quite straightforward, 3-4 lines of quite intuitive code
4. By pipeline methods, we mean methods that give new streams on top of other streams.
Implementation here is also quite intuitive, a pipeline pattern where the output of the underlying stream is altered by the stream on top.
5. I have categorized some methods as early exit.
This means that the result may be obtained without visiting all the items.
We could of course let the iteration complete, but it is suboptimal.
A quite simple extension to the iterator interface is
There are some inherent drawbacks with the iterator pattern.
The iterator instance has state and direct references back to the provider and its internal state.
Implementation is often difficult, complex code is more error prone.
For me this is the most important drawback.
You can of course cheat, as I have done many times. Write a recursive method to return a collection of items and pass an iterator to that collection instead.
It may be an ok solution for 100 items, but less so if there are a million.
The iterator is limited to sequential execution