What's a for-each loop in Java?
A for-each is a kind of for loop that you use when you need to process all the elements of an array or collection in Java. That said, the phrase for-each is not actually used in this loop. Its syntax is as follows:
for (type itVar : array)
{
// Operations
}
Where type is the type of the iterator variable (which matches the datatype of elements in the array!), itVar is its name, and array is an array (other data structures are also allowed, for example, some sort of collection, like ArrayList), i.e. the object on which the loop is executed. As you can see, this construct does not use a counter: the iterator variable simply iterates over the elements of the array or collection. When such a loop is executed, the iterator variable is sequentially assigned the value of each element of the array or collection, after which the specified block of statements (or statement) is executed.
In addition to the for-each loop, Java also has a forEach() method. You can read about it in the article entitled "Stop writing loops!" Top 10 best practices for working with collections in Java 8 |
for (int i=0; i < array.length; i++)
{
// Statements
}
Example of a for-each loop
We create an array of student scores. Then we use a for-each loop to print out all the estimates, calculate the average score, and find the top score.
public class ForEachTest {
// A method that prints all scores
public static void printAllScores(int[] scores) {
System.out.print("|");
for (int num : scores) {
System.out.print(num + "|");
}
System.out.println();
}
// A method that displays the average score
public static double getAverageScore(int[] numbers) {
int totalScore = 0;
for (int num : numbers) {
totalScore = num + totalScore;
}
return ((double) totalScore / numbers.length);
}
// A method that determines the best (maximum) score
public static int getBestScore(int[] numbers) {
int maxScore = numbers[0];
for (int num : numbers) {
if (num > maxScore) {
maxScore = num;
}
}
return maxScore;
}
public static void main(String[] args) {
// Array of scores
int[] scores = {5, 10, 7, 8, 9, 9, 10, 12};
int bestScore = getBestScore(scores);
System.out.print("All the scores: ");
printAllScores(scores);
System.out.println("The highest score is " + bestScore);
System.out.println("The average score is " + getAverageScore(scores));
}
}
Program output:
All the scores: |5|10|7|8|9|9|10|12|
The highest score is 12
The average score is 8.75
Now, let's see what a method for printing all the scores would look like if we used an ordinary for loop:
public static void printAllScores(int[] scores) {
System.out.print("|");
for (int i = 0; i < scores.length; i++) {
System.out.print(scores[i] + "|");
}
System.out.println();
}
If we call this method from the main method, then we get this result:
All the scores: |5|10|7|8|9|9|10|12|
Example of a for-each loop with collections
We create a collection of names and display all the names on the screen.
List<String> names = new ArrayList<>();
names.add("Snoopy");
names.add("Charlie");
names.add("Linus");
names.add("Shroeder");
names.add("Woodstock");
for(String name : names){
System.out.println(name);
}
Limitations of a for-each loop
The for-each loop's compact form is considered easier to read than a for loop, and it is considered best practice to use a for-each loop wherever possible. However, a for-each loop is a less universal construct than an ordinary for loop. Here are some simple cases where a for-each loop either won't work at all or will work, but only with difficulty.If you want to run through a loop from the end to the beginning. That is, there is no for-each loop that is a direct analogue to the following code:
for (int i= array.length-1; i>0; i--) { System.out.println(array[i]); }For-each is not suitable if you want to make changes to an array. For example, you can't sort an array without changing the location of its elements. Additionally, in the following code, only the iterator variable will change, not the element of the array:
for (int itVar : array) { itVar = itVar++; }If you are looking for an element in an array and you need to return (or pass on) the index of the element you are looking for, then it is better to use an ordinary for loop.
A helpful video about the for-each loop
Using Lambda Expressions with For-each Loops
Lambda expressions provide a concise way to represent functions and can be seamlessly integrated with for-each loops for improved readability and functionality. This is particularly useful when iterating over collections in Java.
Example: Using Lambda Expressions
import java.util.Arrays;
import java.util.List;
public class LambdaExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Using a for-each loop with a lambda expression
names.forEach(name -> System.out.println("Hello, " + name));
}
}
In this example, the lambda expression name -> System.out.println("Hello, " + name) replaces the need for a traditional loop. It enhances code clarity and minimizes boilerplate.
Why Use Lambda Expressions?
- Conciseness: Reduces the verbosity of code compared to traditional loops.
- Flexibility: Easily integrates with Java's functional programming features, such as streams.
- Modern Syntax: Aligns with best practices introduced in Java 8 and beyond.
Introducing Method References in For-each Loops
Method references are a shorthand notation for lambda expressions that refer to a method by name. They make the code even more concise and readable while maintaining functionality.
Types of Method References
- Static Method Reference:
ClassName::methodName - Instance Method Reference:
instanceName::methodName - Constructor Reference:
ClassName::new
Example: Using Method References
import java.util.Arrays;
import java.util.List;
public class MethodReferenceExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Using a method reference with a for-each loop
names.forEach(System.out::println);
}
}
Here, System.out::println is a method reference that simplifies the lambda expression name -> System.out.println(name). This makes the code more expressive and easier to read.
When to Use Method References
- Readability: Use method references when they make the code cleaner and more understandable.
- Reusability: Method references are ideal for reusing existing methods in functional contexts.
- Streamlined Syntax: When a lambda expression simply calls an existing method, prefer a method reference for brevity.
Combining Lambda Expressions and Method References
While lambda expressions and method references can be used independently, combining them with for-each loops provides the ultimate flexibility for writing concise, readable, and maintainable Java code.
Example: Mixing and Matching
import java.util.Arrays;
import java.util.List;
public class CombinedExample {
public static void main(String[] args) {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
// Lambda expression for custom logic
names.forEach(name -> System.out.println("Processed: " + name));
// Method reference for standard output
names.forEach(System.out::println);
}
}
In this example, both lambda expressions and method references are used to achieve different goals, showcasing their complementary nature.
Performance Comparison: For-each vs. Traditional For-loops
When comparing the performance of for-each loops to traditional for-loops, it is important to understand the underlying mechanics. For-each loops are designed for simplicity and readability, but they may not always match the performance of traditional for-loops in specific scenarios.
Example: Traditional For-loop
public class TraditionalForLoopExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int i = 0; i < numbers.length; i++) {
System.out.println("Number: " + numbers[i]);
}
}
}
The traditional for-loop provides direct control over the index, allowing optimizations such as skipping elements or iterating in reverse.
Example: For-each Loop
public class ForEachLoopExample {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int number : numbers) {
System.out.println("Number: " + number);
}
}
}
The for-each loop simplifies iteration by abstracting the index management but does not allow manual control over iteration steps.
Performance Insights
- Array Iteration: For-each loops and traditional for-loops perform similarly when iterating over arrays, as both rely on direct index access.
- Collections: For-each loops may introduce slight overhead due to the use of iterators under the hood, whereas traditional for-loops can bypass this by using direct indexing if supported.
- Small Collections: The performance difference is negligible for small datasets, making for-each loops a better choice for readability.
- Large Collections: For extremely large collections, traditional for-loops can offer marginal performance benefits due to reduced iterator overhead.
Scenarios Where For-each Loops May Not Excel
While for-each loops are convenient, they are not always the best choice for every use case. Below are conditions where traditional for-loops may outperform for-each loops:
1. Index-based Access
For-each loops do not provide access to the index of the current element, which is crucial for certain operations:
public class IndexBasedAccess {
public static void main(String[] args) {
String[] words = {"Hello", "World", "Java"};
for (int i = 0; i < words.length; i++) {
if (i % 2 == 0) {
System.out.println("Word: " + words[i]);
}
}
}
}
2. Skipping or Reversing Iteration
For-each loops always iterate sequentially from start to end. If skipping elements or reversing the iteration is required, traditional for-loops are the better choice:
public class ReverseIteration {
public static void main(String[] args) {
int[] numbers = {1, 2, 3, 4, 5};
for (int i = numbers.length - 1; i >= 0; i--) {
System.out.println("Number: " + numbers[i]);
}
}
}
Performance Comparison: For-each Loops vs. Iterators
For-each loops rely on iterators to traverse collections internally. While they simplify the syntax, developers may prefer using iterators explicitly for more control.
Example: Using an Iterator
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorExample {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
System.out.println("Name: " + iterator.next());
}
}
}
Performance Considerations
- Readability: For-each loops offer better readability compared to iterators.
- Explicit Control: Iterators allow modification of the collection during iteration, whereas for-each loops do not.
- Performance: The performance difference between for-each loops and explicit iterators is minimal, but iterators can be more flexible for complex operations.
Loops in the CodeGym courseOn CodeGym, we start practicing using loops at Level 4 of the Java Syntax quest. Several of the lessons in that level, as well as many of the tasks in various levels, are devoted to loops in order to reinforce your skills in working with them. Basically, there's no way you can escape them — loops are one of the most important constructs in programming. |
More information about for-each and other loops
- The while statement. The article is about to the simplest kind of loop: the
whileloop, which CodeGym uses to introduce loops to students. - Stop writing loops! Top 10 best practices for working with collections in Java 8. This article will help CodeGym students who are at least halfway through the course learn many interesting things about working with collections.
GO TO FULL VERSION