1. Overview
The for-each loop is an elegant and simple tool when we iterate over a List. Sometimes, there are scenarios where we need to perform specific actions or make decisions based on whether an iteration is the last one.
In this tutorial, we’ll discuss this scenario and explore different ways to determine if the current iteration is the last one when we loop a List.
2. Introduction to the Problem
First, let’s create a List of movie titles as our input example:
List<String> MOVIES = List.of(
"Titanic",
"The Deer Hunter",
"Lord of the Rings",
"One Flew Over the Cuckoo's Nest",
"No Country For Old Men");
If we merely want to obtain the last element after a typical for-each loop, it isn’t a challenge. We can simply reassign the same variable with the element in each iteration. After we walk through the entire list, the variable holds the last element:
String myLastMovie = "";
for (String movie : MOVIES) {
// do some work with movie
myLastMovie = movie;
}
assertEquals("No Country For Old Men", myLastMovie);
However, sometimes, we need to perform some particular actions only in the last iteration. So, during the looping, we must identify the last iteration within a for-each loop.
Unlike traditional for loops, where an index variable readily indicates the current iteration, the for-each loop conceals this information. Therefore, the absence of an explicit loop index poses a challenge to determine the last iteration.
Next, let’s see how to solve this problem.
3. Using IntStream
We know that solving the problem using a traditional for loop isn’t difficult since we know the index of the current iteration:
int size = MOVIES.size();
String myLastMovie = null;
for (int i = 0; i < size; i++) {
String movie = MOVIES.get(i)
// ... work with movie
if (i == size - 1) {
myLastMovie = movie;
}
}
assertEquals("No Country For Old Men", myLastMovie);
We can follow the same idea and create an IntStream containing all indexes of the list elements. Then, we can use the forEach() method to loop through the indexes and check whether the current is the last iteration, the same as in a traditional for loop:
int size = MOVIES.size();
Map<Integer, String> myLastMovie = new HashMap<>();
IntStream.range(0, size)
.forEach(idx -> {
String movie = MOVIES.get(idx);
// ... work with movie
if (idx == size - 1) {
myLastMovie.put(idx, movie);
}
});
assertEquals(1, myLastMovie.size());
assertTrue(myLastMovie.containsKey(size - 1));
assertTrue(myLastMovie.containsValue("No Country For Old Men"));
As we can see in the test, we used the IntStream.range(0, list.size()) method to initialize the list’s index stream. It’s worth noting that we used a Map object to store the last index and value for later verifications. This is because local variables used in lambdas must be final or effectively final.
4. Using an External Counter
Alternatively, we can maintain an external counter variable to keep track of the iteration count. By comparing the counter with the size of the collection, we can determine the last iteration:
int size = MOVIES.size();
String myLastMovie = null;
int cnt = 0;
for (String movie : MOVIES) {
// ... work with movie
if (++cnt == size) {
myLastMovie = movie;
}
}
assertEquals("No Country For Old Men", myLastMovie);
Compared to the IntStream solution, this approach is more straightforward.
5. Looping With an Iterator
Although we have solved the problem in the IntStream and counter approaches, both solutions require introducing new variables and performing additional comparisons to detect the last iteration.
Next, let’s see how we can determine the last iteration while traversing the list using an Iterator.
Iterator provides the hasNext() method, allowing us to check if the current iteration is the last conveniently. Therefore, we don’t need additional variables for this purpose:
String movie;
String myLastMovie = null;
for (Iterator<String> it = MOVIES.iterator(); it.hasNext(); ) {
movie = it.next();
// ... work with movie
if (!it.hasNext()) { // the last element
myLastMovie = movie;
}
}
assertEquals("No Country For Old Men", myLastMovie);
As we can see, using an Iterator is an optimal choice to solve this problem.
6. Conclusion
In this article, we’ve explored three approaches to identifying the last iteration while traversing a List using a for-each loop.
With its built-in hasNext() check, the Iterator approach can be an optimal solution to address this particular challenge.
As always, the complete source code for the examples is available over on GitHub.