All of you who started learning Java (just recently of ages ago) probably started with variables, operators and went to the next building block – if statements and loops. We’ve got a regular for loop there, followed by the while and do-while loops. But beware, since Java 5 we’ve got a new addition to the loop family – the baby but a powerful one. “For-each” loop is the cool kind on the block because it looks neat and clean while still packing a powerful punch with whatever you need it to do. Ready to meet the new girl? Come on, let’s get started!
Main features
The “for-each” loop, also known as the “enhanced for loop“, is a type of loop construct in Java that allows you to iterate over elements in a collection or an array. It is designed to simplify the syntax and make the code more readable.
The basic syntax of a for-each loop is:
for (element : collection) {
// Do something with element
}
Some of the key features of the for-each loop include:
- It automatically handles the index variable, which can be a source of bugs and errors in traditional for loops.
- It supports iterating over any object that implements the Iterable interface, including collections and arrays.
- It supports iterating over primitive arrays, as well as objects.
#iterator
Under the hood, the for-each loop is implemented using the Iterator interface, which allows it to iterate over any object that implements this interface. When the loop is executed, it retrieves an iterator for the collection or array, and then uses the hasNext() and next() methods of the iterator to iterate over the elements.
#designPatterns
Some of the design patterns that are commonly used with the for-each loop include the Iterator pattern and the Observer pattern, both of which are based on the Iterable interface.
#bestPractices
– It’s important to avoid modifying the collection or array while iterating over it, as this can lead to bugs and errors.
– Use the for-each loop when iterating over collections or arrays, but use a traditional for loop when you need to iterate over a range of values or perform complex operations on each element.
What is the power of the for-each loop?
The for-each loop is a powerful language construct in Java for several reasons:
- Conciseness: The for-each loop allows you to iterate over the elements of a collection or array using a simple and concise syntax, without the need for an explicit index variable or loop counter. This can make your code easier to read and maintain, especially when dealing with nested loops or complex data structures.
- Readability: The for-each loop can make your code more readable and expressive, by focusing on the elements of the collection or array rather than on the mechanics of the iteration.
- Type safety: The for-each loop provides type safety because it automatically casts each element of the collection or array to the correct type, based on the type of the collection or array. This can help to catch errors and bugs at compile time, rather than at runtime.
- Performance: In many cases, the for-each loop is more efficient than a traditional for loop, especially when iterating over large collections or arrays. This is because the for-each loop uses an optimised iteration protocol that avoids unnecessary object creations and memory allocations.
- Ease of use: The for-each loop is easy to use, and requires no special knowledge or training to use effectively. This can make it a valuable tool for both novice and experienced Java developers.
Limitations of the for-each loop:
- It does not provide access to the current index of the element, which may be needed in some cases.
- It does not provide a way to modify the collection or array while iterating over it, which can be a problem in some situations.
- It cannot be used to iterate over multiple collections or arrays at once.
Cool things to know
- When using the for-each loop with collections, you can specify the type of elements that the collection contains using a generic type or a type parameter.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
for (String name : names) { //generic type
System.out.println(name);
}
int[] numbers = {1, 2, 3};
for (int number : numbers) { //type parameter
System.out.println(number);
}
- The for-each loop can be used with custom classes as long as they implement the Iterable interface. To do this, you need to define an iterator method in the class that returns an instance of an Iterator.
public class MyIterableClass implements Iterable<String> {
private List<String> items = new ArrayList<>();
public void addItem(String item) {
items.add(item);
}
public Iterator<String> iterator() { //there you have it!
return items.iterator();
}
}
MyIterableClass iterable = new MyIterableClass();
iterable.addItem("foo");
iterable.addItem("bar");
for (String item : iterable) {
System.out.println(item);
}
- To iterate over a collection or array in reverse order using the for-each loop, you can use the Collections.reverse() or Arrays.stream() methods. For example:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.reverse(names);
for (String name : names) {
System.out.println(name);
}
int[] numbers = {1, 2, 3};
for (int number : Arrays.stream(numbers).boxed().sorted(Comparator.reverseOrder()).toArray(Integer[]::new)) {
System.out.println(number);
}
- To remove elements from a collection while iterating over it using the for-each loop, you can use the Iterator.remove() method. This method is designed specifically for removing elements from a collection while iterating over it, and ensures that the collection is modified safely and without causing any bugs.
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
for (Iterator<String> iterator = names.iterator(); iterator.hasNext();) {
String name = iterator.next();
if (name.startsWith("A")) {
iterator.remove();
}
}
- When using the for-each loop with arrays, you can use the Java 8 Streams API to perform more complex operations on each element.
int[] numbers = {1, 2, 3};
int sum = Arrays.stream(numbers).map(n -> n * 2).sum();
System.out.println(sum);
- One common issue with the for-each loop is concurrent modification, where the collection or array is modified by another thread while it is being iterated over. To avoid this, you can use a synchronized block or a concurrent collection. For example:
List<String> names = new ArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
synchronized (names) {
for (String name : names) {
System.out.println(name);
}
}
List<String> concurrentNames = new CopyOnWriteArrayList<>(Arrays.asList("Alice", "Bob", "Charlie"));
for (String name : concurrentNames) {
System.out.println(name);
}
- If you need to iterate over multiple collections or arrays at once, you can use the Java 8 Streams API to combine them. For example:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
int[] ages = {25, 30, 35};
IntStream.range(0, Math.min(names.size(), ages.length))
.forEach(i -> System.out.println(names.get(i) + " is " + ages[i] + " years old."));
- In some cases, you may need to access the current index of the element while using the for-each loop. To do so, use the IntStream.range() method and the index variable of the lambda expression. For example:
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.forEach((name, i) -> System.out.println(i + ": " + name));
- If you’re using for-each loop to iterate over an array of a collection that may contain nulls, use it with with Optionals.
List<String> names = Arrays.asList("Alice", null, "Charlie");
names.stream().filter(Objects::nonNull).forEach(System.out::println);
- Use the for-each loop with parallel streams to improve the performance of operations that can be executed in parallel. Just make sure to avoid thread-safety issues and ensure that the operations are suitable for parallel execution.
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
names.parallelStream().forEach(System.out::println);
when is a regular for loop better?
While the for-each loop is a convenient and expressive way to iterate over collections and arrays in Java, sometimes a plain ol’ for loop may be more suitable. Here are some situations where it’s better to use a regular for loop:
- When you need the access to the index of the element: The for-each loop does not expose the index of the element, so if you need to access the index for some reason, such as updating the element or iterating over multiple collections in parallel, a regular for loop is a better choice.
- When you need to modify the collection or array during iteration: The for-each loop does not allow you to modify the collection or array during iteration, such as adding or removing elements. If you need to modify the collection, a regular for loop with an iterator or an index variable is a better choice.
- When you need to iterate over a subset of the collection or array: The for-each loop iterates over the entire collection or array, so if you need to iterate over a subset of the elements, such as the first n elements or elements that match a specific condition, a regular for loop with an if statement or a break statement is a better choice.
- When you need to iterate over a primitive array: The for-each loop only works with collections and arrays that implement the Iterable interface, so if you need to iterate over a primitive array such as int[], double[], or boolean[], a regular for loop with an index variable is a better choice.
- When you need to optimise performance: In some cases, the for-each loop may be less performant than other loop constructs, especially when using parallel streams or iterating over large collections. If performance is a concern, a regular for loop may be a better choice, as it allows more control over the iteration process and can be optimized for specific scenarios.
Some more things to remember
- When using the for-each loop with collections, make sure the collection implements the Iterable interface and has a defined iteration order, as some collections such as HashSet and HashMap do not have a defined order.
- When using the for-each loop with arrays, make sure the array is not null and has a defined length, as accessing a null or uninitialized array can cause a NullPointerException or an ArrayIndexOutOfBoundsException.
- When using the for-each loop with streams, be aware of the potential risk of ConcurrentModificationExceptions when modifying the stream during iteration, and consider using parallel streams for performance optimization.
- When using the for-each loop with streams, be aware of the lazy evaluation and short-circuiting behaviour of intermediate operations, and make sure the terminal operation is called to trigger the evaluation of the stream.
- When using the for-each loop with streams, consider using the Java 8 Optional API to handle null values and avoid NullPointerExceptions.
- When using the for-each loop with maps, consider using the entrySet() method to iterate over the entries of the map, as it is more efficient than iterating over the keys and calling get() for each key.
- When using the for-each loop, use descriptive variable names and break down complex operations into smaller steps to improve readability and maintainability.
- Be mindful of the performance implications of using the for-each loop with large collections or arrays, as the enhanced for loop may be less efficient than a traditional for loop or a stream in certain scenarios. For example, when iterating over an ArrayList, the enhanced for loop is generally faster than a traditional for loop, but slower than a stream.
- Consider using lambdas and functional interfaces with the for-each loop to improve expressiveness and reduce boilerplate code, especially when working with streams. For example, you can use the forEach() method of the Stream class to iterate over the elements of a stream and apply a lambda expression to each element.
- When using the for-each loop with streams, be aware of the different types of operations available, such as intermediate and terminal operations, and choose the appropriate operation based on the requirements of the scenario. For example, filter() is an intermediate operation that returns a new stream containing only the elements that match a specific condition, while forEach() is a terminal operation that applies a lambda expression to each element of the stream.
- When using the for-each loop with streams, be aware of the potential for side effects and synchronisation issues, especially when working with shared resources such as databases or files. Consider using thread-safe collections or synchronisation mechanisms such as locks or semaphores to prevent race conditions and ensure data consistency.
- Consider using the Spliterator interface in Java 8 and later to create custom spliterators for your collections and data structures, and to improve performance and parallelism when using streams. A spliterator is a more powerful iterator that can be split and traversed in parallel, and can be used with the for-each loop by calling the tryAdvance() method.
- Use the break and continue statements with the for-each loop to terminate the loop or skip over elements that meet a specific condition. This can be useful for scenarios where you need to exit the loop early or ignore certain elements.
- Use the forEachRemaining() method of the Iterator interface to process any remaining elements in the iterator after the loop has terminated. This can be useful for scenarios where you need to ensure that all elements are processed, regardless of whether the loop is terminated early.
- Use the forEachOrdered() method of the Stream interface to ensure that the elements of the stream are processed in the order specified by the collection or data structure. This can be useful for scenarios where the order of the elements is important, such as when processing a sequence of events.
- Use the parallelStream() method of the Collection interface to process the elements of a collection or array in parallel, and to improve performance on multi-core processors. This can be useful for scenarios where the collection or array is large and the processing of each element is independent.

/*
Until next time,
stay positive (like unsigned integer) and keep coding!
eMs
*/
