Showing posts with label iterable. Show all posts
Showing posts with label iterable. Show all posts

Friday, 27 May 2011

Collection views for Maps for iteration

The Collection-view methods allow a Map to be viewed as a Collection in three ways:
  • keySet: the Set of keys contained in the Map.
  • values: The Collection of values contained in the Map. This Collection is not a Set, as multiple keys can map to the same value.
  • entrySet: The Set of key-value pairs contained in the Map. The Map interface provides a small nested interface called Map.Entry that is the type of the elements in this Set.

The Collection-views provide the only means to iterate over a Map.

Iterating over the keys in a Map

Here's an example illustrating the standard idiom for iterating over the keys in a Map:

for (Iterator i=m.keySet().iterator(); i.hasNext(); )
System.out.println(i.next());


Iterating over the values in Map

The idiom for iterating over values is analogous.

for (Iterator i=m.values().iterator(); i.hasNext(); )
System.out.println(i.next());


Iterating over the key value pairs

 

Here's the idiom for iterating over key-value pairs:

for (Iterator i=m.entrySet().iterator(); i.hasNext(); ) {
Map.Entry e = (Map.Entry) i.next();
System.out.println(e.getKey() +
": " + e.getValue());
}

When first presented with these idioms, many people worry that they may be slow because the Map has to create a new Collection object each time a Collection-view operation is called. Rest easy: This is not the case. There's no reason that a Map can't always return the same object each time it is asked for a given Collection-view. This is precisely what all of the JDK's Map implementations do.
With all three Collection-views, calling an Iterator's remove operation removes the associated entry from the backing Map (assuming the Map supports element removal to begin with). With the entrySet view, it is also possible to change the value associated with a key, by calling a Map.Entry's setValue method during iteration (again, assuming the Map supports value modification to begin with). Note that these are the only safe ways to modify a Map during iteration; the behavior is unspecified if the underlying Map is modified in any other way while the iteration is in progress.
The Collection-views support element removal in all its many forms: the remove, removeAll, retainAll, and clear operations, as well as the Iterator.remove operation. (Yet again, this assumes that the backing Map supports element removal.)
The Collection-views do not support element addition under any circumstances. It would make no sense for the keySet and values views, and it's unnecessary for the entrySet view, as the backing Map's put and putAll provide the same functionality.


Wednesday, 2 February 2011

What is iterator?

The point of using an iterator is to allow the the client to iterate over the collection of objects, without exposing implementation details. This gives you the benefit to change what collection the Iterator iterates over and how each element is served, without making any change the client's code.


Making custom collections iterable

Let's say we have our own custom Vector-based collection, GameCollection, that stores Games and we want to make this collection iterable (over its stored Games) by making it implement the Iterator inteface:


  1. public class GameCollection implements Iterable<Game> {  
  2.  private Vector<Game> games;  
  3.    
  4.  public GameCollection() {  
  5.   games = new Vector<Game>();  
  6.  }  
  7.    
  8.  public void add(Game game) {  
  9.   games.add(game);  
  10.  }  
  11.   
  12.  @Override  
  13.  public Iterator<Game> iterator() {  
  14.   return games.iterator();  
  15.  }  
  16. }  

The client can then use the above collection as follows:


  1. GameCollection gc = new GameCollection();  
  2. Iterator<game> gameIterator = gc.iterator();  
  3.   
  4. while (gameIterator.hasNext()) {  
  5.  Game g = gameIterator.next();  
  6.  System.out.println(g.getName());  
  7. }  

Or better yet, make use of Java's for-each statement:


  1. GameCollection gc = new GameCollection();  
  2. for (Game g : gc) {  
  3.  System.out.println(g.getName());  
  4. }  

As you can see from the above code, the client doesn't know that we are using a Vector to store our Games in the GameCollection. Infact, we can later change the GameCollection to store the Games in a LinkedList or an Array, and yet the client wouldn't need to change his iteration code (code above) to suit for our changes.

Multiple iterators for a custom collection

What if we now want our GameCollection to offer two iterable solutions? Say we want our GameCollection to iterate over both Games and also GameConsoles?

The first thing that comes to mind is to try and make the GameCollection implement two Iterator interfaces using different generic arguments:


  1. public class GameCollection implements Iterable<Game>, Iterable<GameConsole>  

but unfortunately, the above doesn't compile. Therefore, as a workaround to such an issue, we can make use of Java's ability to allow for inner classes:


  1. public class GameCollection  {  
  2.  private Vector<Game> games;  
  3.  private Vector<GameConsole> consoles;  
  4.    
  5.  private class Games implements Iterable<Game> {  
  6.   @Override  
  7.   public Iterator<Game> iterator() {  
  8.    return games.iterator();  
  9.   }  
  10.  }  
  11.    
  12.  private class Consoles implements Iterable<GameConsole> {  
  13.   @Override  
  14.   public Iterator<GameConsole> iterator() {  
  15.    return consoles.iterator();  
  16.   }  
  17.     
  18.  }  
  19.    
  20.  public GameCollection() {  
  21.   games = new Vector<Game>();  
  22.   consoles = new Vector<GameConsole>();  
  23.  }  
  24.    
  25.  public void add(Game game) {  
  26.   games.add(game);  
  27.  }  
  28.    
  29.  public void add(GameConsole console) {  
  30.   consoles.add(console);  
  31.  }  
  32.   
  33.  public Games games() {  
  34.   return new Games();  
  35.  }  
  36.    
  37.  public Consoles consoles() {  
  38.   return new Consoles();  
  39.  }  
  40. }  

With the above two inner classes and the public methods games() and consoles(), the client can iterate over the collections like such:


  1. GameCollection gc = new GameCollection();  
  2.   
  3. //Add games and consoles with gc.add()  
  4.   
  5. for (Game g : gc.games()) {  
  6.  System.out.println(g.getName());  
  7. }  
  8.   
  9. for (GameConsole g : gc.consoles()) {  
  10.  System.out.println(g.getName());  
  11. }  

Creating custom iterators

Up till this point, we have only used iterators that are in built with Java's existing collections; but what if we want our own custom iterator? We can use Java's java.util.Iterator interface to build our own iterator.

In the following example, I have created a Circular Iterator:
  1. public class CircularGamesIterator implements Iterator<Game> {  
  2.   
  3.  private Vector<Game> list;  
  4.  private int currentPosition;  
  5.    
  6.  public CircularGamesIterator(Vector<Game> games) {  
  7.   list = games;  
  8.   currentPosition = 0;  
  9.  }  
  10.    
  11.  @Override  
  12.  public boolean hasNext() {  
  13.   return currentPosition < list.size();  
  14.  }  
  15.   
  16.  @Override  
  17.  public Game next() {  
  18.   Game el = list.elementAt(currentPosition);  
  19.   currentPosition = (currentPosition + 1) % list.size();   
  20.   return el;  
  21.  }  
  22.   
  23.  @Override  
  24.  public void remove() { }  
  25. }  


The iterator() method of the GameCollection class can then be modified to return an instance of the CircularGamesIterator:


  1. public class GameCollection implements Iterable<Game> {  
  2.  private Vector<Game> games;  
  3.    
  4.  public GameCollection() {  
  5.   games = new Vector<Game>();  
  6.  }  
  7.    
  8.  public void add(Game game) {  
  9.   games.add(game);  
  10.  }  
  11.   
  12.  @Override  
  13.  public Iterator<Game> iterator() {  
  14.   return new CircularGamesIterator(games);  
  15.  }  
  16. }  

An Iterator should not be Iterable!

I have seen some examples on the internet where people make Iterators iterable by making their Iterators implement the Iterator interface and returning this in the iterator() method:
I have removed the implementation in the methods for the following code for brevity.
  1. public class CustomIterator implements Iterator<Game>, Iterable<Game> {  
  2.   
  3.  public CustomIterator(Vector<Game> games) {  
  4.   list = games;  
  5.  }  
  6.   
  7.  @Override  
  8.  public Iterator<Game> iterator() {  
  9.   return this;  
  10.  }  
  11.   
  12.  @Override  
  13.  public boolean hasNext() {  
  14.    //Implementation  
  15.  }  
  16.   
  17.  @Override  
  18.  public Game next() {  
  19.    //Implementation  
  20.  }  
  21.   
  22.  @Override  
  23.  public void remove() { }  
  24. }  


The problem with the above code can be clearly illustrated with such a method:


  1. public static void iterateTwice(Iterable<Game> games) {  
  2.  for (Game g : games) {  
  3.   System.out.println(g.getName());  
  4.  }  
  5.    
  6.  for (Game g : games) {  
  7.   System.out.println(g.getName());  
  8.  }  
  9.  System.out.println();  
  10. }  

If you directly pass in your iterable iterator to the above method, the Games will only be printed once.

To illustrate this, let's assume that we will use the custom iterator mentioned above, CustomIterator, that implements both Iterator<Game> and Iterable<Game>. Our GameCollection implements Iterable<Game>, and its iterator() method returns new CustomIterator(games);. Now, let's also say that GameCollection also provides a method games() that also returns new CustomIterator(games);.

Here's an example that illustrates this:

  1. GameCollection gc = new GameCollection();  
  2.   
  3. gc.add(new Game("Broken Sword"));  
  4. gc.add(new Game("Grim Fandango"));  
  5. gc.add(new Game("Manic Mansion"));  
  6.   
  7. System.out.println("Printing the iterable collection, GameCollection");  
  8. iterateTwice(gc);  
  9.   
  10. System.out.println("Printing the iterable iterator, CustomIterator");  
  11. CustomIterator gamesIterator = gc.games();  
  12. iterateTwice(gamesIterator);  

The output of the above code is as follows:
Printing the iterable collection, GameCollection
Broken Sword
Grim Fandango
Manic Mansion
Broken Sword
Grim Fandango
Manic Mansion

Printing the iterable iterator, CustomIterator
Broken Sword
Grim Fandango
Manic Mansion

Notice how when passing the iterable CustomIterator iterator to the method, the Games were only printed once, whereas when we passed in the GameCollection that implements Iterable, the Games were printed twice.

This is because when CustomIterator was passed in, the second for loop terminated immediately.