Iterating over lists

There are several ways of iterating over a list. The first way is to use a traditional for loop with a counter i:
for (int i = 0; i < books.size(); i++) {
   Book book = books.get(i);
   // do something with book
}
This is quite error prone, because there are several places it is possible to make a mistake. For example, is the first element stored at place 0 or place 1? Should i be less than the books.size() or less or equal than the books.size()? Also, the loop will never terminate if one forgets to icrement i.

A somewhat better solution is, to use an iterator.

for (Iterator it = books.iterator(); it.hasNext(); ) {
   Book book = it.next();
   // do something with book
However, the solution is still rather verbose and contains possibilities for errors.

The recommended way to iterate over a list is:

for (Book book : books) {
   // do something with book
}
The solution is very simple an elegant and there is not much opportunity to make a mistake.

Java 8 has introduced lambda expressions, which means one can now use more advanced features on collections and can also easily introduce new types of iterators oneself. A bascic for-loop can be now written as the following:

books.stream().forEach( b -> { /* do something */ });
However, streams have more advanced features than just iterating over a list, e.g., there are methods to select only the elements one wants to have. Consider an implementation of a findBook(String name) operation in Java 7.
public Book findBook(String name) {
   for (Book book : books) {
      if (book.getName().equals(name)) {
         return book;
      }
   }
   return null;
}
versus an implementation in Java 8:
public Book findBook(String name) {
   Optional r = books
      .stream()
      .filter(b -> b.getName().equals(name))
      .findFirst();
   return r.isPresent() ? r.get() : null;
}


Part of Collections
Hubert Baumeister
April 23, 2018