"

Iterator Examples

Iterating over a Collection with an Iterator

For the examples that follow, we will use a list of Player objects, where a Player has a name and a score.

ArrayList<Player> players = new ArrayList<Player>();
players.add(new Player("Pam", 24));
players.add(new Player("Len", 19));
players.add(new Player("Malia", 37));
players.add(new Player("Bob", 13));
players.add(new Player("Rea", 46));


To use an iterator to traverse this list:

Iterator<Player> iter = players.iterator();
while(iter.hasNext()) {
   Player player = iter.next();
    System.out.println(player);
}

 

Note that the Iterator interface is generic, so we must specify what type of objects we are iterating over. The result of the code above is of course, no different from either of the approaches below:

for-each Loop

Indexed Loop

for(Player player : players) {

   System.out.println(player);

}

for(int i = 0; i < players.size(); i++) {

   System.out.println(players.get(i));

}

Most people would not use an iterator to iterator over a collection, they would use the for-each or indexed loop. A good use for an iterator is to remove certain elements as we iterate over it, which we consider in the next section. Interestingly, the for-each loop compiles down to an iterator.

The Iterator interface is an excellent example of object-oriented design, which uses information hiding. An iterator hides the actual storage mechanism of the data. Thus, the user of an iterator does not need to know how the data is stored, it just knows that it can access the next item. For example, with the indexed loop, if you are using an ArrayList, you must use the get method, with other types of collections, there may be some other method to access an item, or no method at all. With an iterator, you don’t need to know these.

Filtering a Collection with an Iterator – Removing Elements

Filtering a collection refers to the idea of finding all the elements in a collection that meet certain criteria. The way we consider it here, is we will remove all elements that meet the criteria from the collection. An Iterator is the preferred way to filter a collection by removing elements.

In the players list considered above, suppose we want to remove all players whose score is less than 20. In this case, we can use the Iterator’s remove method:

Iterator<Player> iter = players.iterator();
while(iter.hasNext() ) {
   Player player = iter.next();
   if(player.getScore() < 20) {
      	iter.remove();
   }
}

 

A common mistake is to use a for-each loop and then using the collection’s remove method. However, such code will fail if the remove method is executed, throwing a ConcurrentModificationException. You cannot modify a collection (add to or remove from) while iterating over it with a for-each loop. Another common mistake is to use an indexed loop, somewhat naively. An example is found in an Appendix. Other approaches to filtering that work are: an indexed loop with a subtle modification of the index inside the loop (bad practice), a while loop, or using an indexed loop traversing the list in reverse order.

Filtering a Collection with an Iterator – Removing & Returning Elements

Suppose you want to remove certain elements from a collection and also return the removed elements in a new collection. Considering the example from above, suppose we want to (a) remove all players whose score is less than 20 from the players list and (b) put those removed players in another list named lowScorePlayers. Note that every time the next method is called, the next element in the collection is retrieved. Thus, if you call next twice inside the loop, you will receive the next two elements, respectively. Thus, if you need the next element more than once in the loop, you must store it in a variable. The correct version is shown in the table below on the left. There, we make one call to next inside the loop, capturing the item in the player variable. Then, player is used twice. Both examples define this list to store the players that are to be stored in a separate list:

ArrayList<Player> lowScorePlayers = new ArrayList<Player>();

Correct

Incorrect

Iterator<Player> iter = players.iterator();

while( iter.hasNext() ) {

    Player player = iter.next();

    if(player.getScore() < 20) {

        iter.remove();

        lowScorePlayers.add(player);

    }

}

// Result

// players: [Pam-24, Malia-37, Rea-46]

//lowScorePlayers: [Len-19, Bob-13]

Iterator<Player> iter = players.iterator();

while(iter.hasNext()) {

    if(iter.next().getScore() < 20) {

iter.remove();

lowScorePlayers.add(iter.next());

    }

}

// Result

// players: [Pam-24, Malia-37, Rea-46]

// lowScorePlayers: [Malia-37, Rea-46]

For the incorrect version, consider the original list of players:

[Pam-24, Len-19, Malia-37, Bob-13, Rea-46]

When the player, “Len-19” is found, it is removed, but then the subsequent call to iter.next() advances to the next player, “Malia-13”, which is added to lowScorePlayers. Then, the loop repeats, where the first next retrieves: “Bob-13, which is removed and the subsequent call to iter.next() adds “Rea-46” to lowScorePlayers. So, we can see that (a) we are putting the wrong players in the list, (b) some players do not have their score checked – we are effectively skipping them, (c) if there has been a player with score less than 20 in the last space, then there is the possibility that the code would throw an exception as the coded tried to add the next element to lowScorePlayers, (d) the loop, in this case only executed 3 times. This is a common mistake. Thus, if you need the current element in the loop more than once, you should store it in a variable with a single call to iter.next as shown in the correct version above.

License

Icon for the Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License

Computer Science II Copyright © by Various is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License, except where otherwise noted.