Java Tutorial
🔍

Java for-each Loop

Java for-each Loop

The Java for-each loop — formally called the enhanced for loop — iterates over every element in an array or collection without requiring an index counter. You get each element directly, the JVM manages the position, and you write half the code you would need with a basic for loop.

The tradeoff is intentional: by giving up the index, you get a loop that is impossible to write with an off-by-one error. That is not a small thing. Off-by-one bugs in basic for loops are among the most common sources of ArrayIndexOutOfBoundsException in fresher code. The for-each loop eliminates that entire category.

What Is the Java for-each Loop?

The for-each loop is a simplified iteration construct introduced in Java 5. It works with any array and any class that implements the java.lang.Iterable interface — which includes all Java Collections Framework types: List, Set, Queue, Deque, and their implementations.

The loop declares a variable that receives each element on every pass. The JVM handles advancing through the collection internally. You never write i++, never reference list.get(i), and never risk accessing an index that does not exist.

What you cannot do with a for-each loop is equally important to understand:

  • You cannot access the current index
  • You cannot modify the collection while iterating (throws ConcurrentModificationException)
  • You cannot iterate backwards
  • You cannot skip elements using custom step increments
  • You cannot replace elements in a List (reading works, writing the position back does not)

When any of these are required, use the basic for loop with an explicit index or an Iterator.

Syntax

for-each Over an Array

Java
1for (ElementType element : array) { 2 // element holds the current value on each pass 3}

for-each Over a Collection

Java
1for (ElementType element : collection) { 2 // works with any List, Set, Queue, or Iterable implementation 3}

for-each with var (Java 10+)

Java
1for (var element : collection) { 2 // type is inferred — cleaner when the type is long or obvious from context 3}

The colon : reads as "in" — for (String name : nameList) reads as "for each String name in nameList." Keeping this reading in mind makes the loop's intent immediately clear when scanning code.

Beginner Examples

Iterating an Array

Java
1public class CourseScoresDemo { 2 3 public static void main(String[] args) { 4 5 int[] scores = {88, 92, 75, 61, 95}; 6 int total = 0; 7 8 // No index management — each score is received directly 9 for (int score : scores) { 10 total += score; 11 } 12 13 double average = (double) total / scores.length; 14 System.out.println("Total: " + total); 15 System.out.printf("Average: %.1f%n", average); 16 } 17}
Output:
Total: 411
Average: 82.2

No scores[i], no i < scores.length, no i++. The loop cannot accidentally access an out-of-bounds index because the index does not exist in the code. This is the for-each loop's primary value over the basic for loop for simple traversal.

Iterating a List

Java
1import java.util.List; 2 3public class CityListDemo { 4 5 public static void main(String[] args) { 6 7 List<String> cities = List.of( 8 "Mumbai", "Bengaluru", "Hyderabad", "Chennai", "Pune" 9 ); 10 11 System.out.println("Tech hub cities:"); 12 for (String city : cities) { 13 System.out.println(" - " + city); 14 } 15 } 16}
Output:
Tech hub cities:
  - Mumbai
  - Bengaluru
  - Hyderabad
  - Chennai
  - Pune

Iterating a Set

A Set has no defined order guarantee (except LinkedHashSet and TreeSet), but the for-each loop works identically regardless of collection type.

Java
1import java.util.Set; 2import java.util.HashSet; 3 4public class UniqueTagsDemo { 5 6 public static void main(String[] args) { 7 8 Set<String> articleTags = new HashSet<>(); 9 articleTags.add("java"); 10 articleTags.add("backend"); 11 articleTags.add("spring-boot"); 12 articleTags.add("java"); // duplicate — Set ignores this 13 articleTags.add("interview"); 14 15 System.out.println("Unique tags (" + articleTags.size() + "):"); 16 for (String tag : articleTags) { 17 System.out.println(" " + tag); 18 } 19 } 20}
Output:
Unique tags (4):
  java
  backend
  spring-boot
  interview

The duplicate "java" entry was ignored by the Set. The for-each loop iterates whatever elements exist — it does not know or care about the internal storage structure.

How the Java for-each Loop Works Internally

What the Compiler Generates

The for-each loop is syntactic sugar. The Java compiler transforms it into different code depending on what is being iterated.

The diagram below shows the two transformation paths.

        for-each loop
              |
    +---------+---------+
    |                   |
  Array             Iterable
    |               (List, Set, etc.)
    |                   |
Compiles to:       Compiles to:
                        |
for (int i = 0;    Iterator<T> it =
     i < arr.length;   collection.iterator();
     i++) {        while (it.hasNext()) {
    T elem =           T elem = it.next();
        arr[i];        // body
    // body        }
}

For arrays, the compiler generates a basic indexed for loop internally — same performance, cleaner source. For Iterable types, the compiler generates an Iterator-based loop using hasNext() and next(). This generated iterator is what causes ConcurrentModificationException when you modify a List while iterating — the iterator detects that the collection's modification count changed since the iterator was created.

Why ConcurrentModificationException Happens

Java
1import java.util.ArrayList; 2import java.util.List; 3 4public class ConcurrentModDemo { 5 6 public static void main(String[] args) { 7 8 List<String> orders = new ArrayList<>(); 9 orders.add("ORD-001"); 10 orders.add("ORD-002"); 11 orders.add("ORD-003"); 12 13 try { 14 for (String order : orders) { 15 if (order.equals("ORD-002")) { 16 // Directly removing from the list while the iterator is active 17 // causes the iterator's internal check to detect a modification 18 orders.remove(order); 19 } 20 } 21 } catch (java.util.ConcurrentModificationException ex) { 22 System.out.println("ConcurrentModificationException thrown."); 23 System.out.println("Cannot modify a List directly during for-each iteration."); 24 } 25 26 System.out.println("Safe approach: use removeIf() or Iterator.remove()"); 27 orders.removeIf(order -> order.equals("ORD-002")); 28 System.out.println("Remaining orders: " + orders); 29 } 30}
Output:
ConcurrentModificationException thrown.
Cannot modify a List directly during for-each iteration.
Safe approach: use removeIf() or Iterator.remove()
Remaining orders: [ORD-001, ORD-003]

The correct approaches for removal during iteration are: list.removeIf(predicate) introduced in Java 8 for simple filter-and-remove, and explicit Iterator with iterator.remove() for more complex logic.

Real-World Example — Product Catalog Price Auditor

The Business Problem

You are building a price audit service for a grocery delivery platform — similar to what Zepto or Blinkit uses for its catalog management. The service must scan all products in a category, identify those priced below the minimum margin threshold, and generate an alert list for the pricing team. The catalog is stored as a List<Product>, and every product must be checked.

There is no index logic needed — the service does not care about position, only values. No elements are removed during the scan. Every product is read once. These conditions describe the exact scenario where for-each is the correct and cleanest choice.

Implementation

Java
1// File: Product.java 2 3public class Product { 4 5 private final String productName; 6 private final String category; 7 private final double sellingPrice; 8 private final double minimumMarginPrice; 9 10 public Product(String productName, String category, 11 double sellingPrice, double minimumMarginPrice) { 12 this.productName = productName; 13 this.category = category; 14 this.sellingPrice = sellingPrice; 15 this.minimumMarginPrice = minimumMarginPrice; 16 } 17 18 public String getProductName() { return productName; } 19 public String getCategory() { return category; } 20 public double getSellingPrice() { return sellingPrice; } 21 public double getMinimumMarginPrice() { return minimumMarginPrice; } 22 23 public boolean isBelowMinimumMargin() { 24 return sellingPrice < minimumMarginPrice; 25 } 26}
Java
1// File: PriceAuditService.java 2 3import java.util.ArrayList; 4import java.util.List; 5 6public class PriceAuditService { 7 8 public List<String> auditCatalog(List<Product> catalog) { 9 10 List<String> alerts = new ArrayList<>(); 11 12 // for-each is the correct choice — reading every element, no index needed 13 for (Product product : catalog) { 14 if (product.isBelowMinimumMargin()) { 15 String alert = String.format( 16 "ALERT: %s [%s] — Selling at Rs.%.2f below margin of Rs.%.2f", 17 product.getProductName(), 18 product.getCategory(), 19 product.getSellingPrice(), 20 product.getMinimumMarginPrice() 21 ); 22 alerts.add(alert); 23 } 24 } 25 26 return alerts; 27 } 28}
Java
1// File: CatalogAuditDemo.java 2 3import java.util.List; 4 5public class CatalogAuditDemo { 6 7 public static void main(String[] args) { 8 9 List<Product> groceryCatalog = List.of( 10 new Product("Organic Milk 1L", "Dairy", 62.0, 65.0), 11 new Product("Whole Wheat Bread", "Bakery", 45.0, 40.0), 12 new Product("Cold Pressed Oil 1L","Oils", 185.0, 200.0), 13 new Product("Greek Yogurt 400g", "Dairy", 89.0, 85.0), 14 new Product("Chia Seeds 250g", "Superfoods", 149.0, 160.0) 15 ); 16 17 PriceAuditService auditor = new PriceAuditService(); 18 List<String> alerts = auditor.auditCatalog(groceryCatalog); 19 20 if (alerts.isEmpty()) { 21 System.out.println("All products are priced above minimum margin."); 22 } else { 23 System.out.println("Price alerts (" + alerts.size() + " products):"); 24 for (String alert : alerts) { 25 System.out.println(alert); 26 } 27 } 28 } 29}
Output:
Price alerts (3 products):
ALERT: Organic Milk 1L [Dairy] — Selling at Rs.62.00 below margin of Rs.65.00
ALERT: Cold Pressed Oil 1L [Oils] — Selling at Rs.185.00 below margin of Rs.200.00
ALERT: Chia Seeds 250g [Superfoods] — Selling at Rs.149.00 below margin of Rs.160.00

Two for-each loops appear here — one in auditCatalog to scan every product, and one in main to print every alert. Both are read-only passes over the entire collection with no index logic. Using a basic for with catalog.get(i) would have been syntactically noisier without adding anything.

The isBelowMinimumMargin() method on Product keeps the pricing logic inside the domain object rather than scattered across the caller. During code reviews, price calculations embedded directly inside loop bodies are consistently flagged — they belong in the model.

for-each vs Basic for Loop — When to Choose

CriterionUse for-eachUse basic for
Need every element in sequenceYesYes
Need the current indexNo — use basic forYes
Need to modify collection during iterationNo — use IteratorYes (with care)
Need to iterate backwardsNoYes
Need a step size other than 1NoYes
Iterating arrays — clean read-only passYesYes (more verbose)
Iterating List, Set, QueueYesYes (requires .get() for List)
Code review readability preferenceFor-each preferredRequired when index matters

Best Practices

Default to for-each for read-only iteration

When you need every element and have no reason to track position, for-each is the right choice. It is shorter, eliminates off-by-one risk, and signals clearly to other developers that this is a simple full-pass read. Teams following clean code conventions treat for-each as the default and basic for as the explicit choice when index access is genuinely required.

Use var with for-each when the type is long or obvious (Java 10+)

Java
1// Verbose but explicit 2for (Map.Entry<String, List<OrderLineItem>> entry : orderMap.entrySet()) { 3 // body 4} 5 6// Cleaner with var — type is inferred, readability improves 7for (var entry : orderMap.entrySet()) { 8 // body 9}

Use removeIf or collect-then-remove instead of removing inside for-each

Java
1// Throws ConcurrentModificationException 2for (String item : list) { 3 if (shouldRemove(item)) list.remove(item); // runtime error 4} 5 6// Clean — filter using removeIf (Java 8+) 7list.removeIf(item -> shouldRemove(item)); 8 9// Or collect items to remove, then remove after the loop 10List<String> toRemove = new ArrayList<>(); 11for (String item : list) { 12 if (shouldRemove(item)) toRemove.add(item); 13} 14list.removeAll(toRemove);

Do not use for-each when you need the index for business logic

A mistake that appears often in fresher pull requests is using for-each and then manually tracking a counter variable inside the body because the index is needed. At that point, a basic for loop was the right choice from the start.

Java
1// Anti-pattern — manual counter inside for-each 2int position = 0; 3for (String item : itemList) { 4 System.out.println(position + ": " + item); 5 position++; // manually tracking what the basic for loop provides for free 6} 7 8// Correct — basic for loop when index is needed 9for (int i = 0; i < itemList.size(); i++) { 10 System.out.println(i + ": " + itemList.get(i)); 11}

Common Mistakes

Mistake 1 — Modifying the Collection During Iteration

Java
1import java.util.ArrayList; 2import java.util.List; 3 4public class ModifyDuringIterationDemo { 5 6 public static void main(String[] args) { 7 8 List<String> tags = new ArrayList<>(List.of("java", "spring", "debug-me", "interview")); 9 10 // This throws ConcurrentModificationException at runtime 11 try { 12 for (String tag : tags) { 13 if (tag.startsWith("debug")) { 14 tags.remove(tag); // modifying the backing list while iterator is active 15 } 16 } 17 } catch (java.util.ConcurrentModificationException ex) { 18 System.out.println("Exception: " + ex.getClass().getSimpleName()); 19 } 20 21 // Safe alternative — removeIf handles this correctly 22 tags.removeIf(tag -> tag.startsWith("debug")); 23 System.out.println("Tags after safe removal: " + tags); 24 } 25}
Output:
Exception: ConcurrentModificationException
Tags after safe removal: [java, spring, interview]

Mistake 2 — Expecting for-each to Work on Non-Iterable Types

Java
1// This does not compile — int is a primitive, not an Iterable or array of objects 2int total = 50000; 3 4// for (int digit : total) { } — compile error 5 6// Maps are also not directly iterable — you must iterate their views 7import java.util.Map; 8import java.util.HashMap; 9 10Map<String, Integer> scores = new HashMap<>(); 11scores.put("Alice", 92); 12scores.put("Bob", 78); 13 14// This does not compile — Map does not implement Iterable 15// for (var entry : scores) { } — compile error 16 17// Correct — iterate the entry set 18for (Map.Entry<String, Integer> entry : scores.entrySet()) { 19 System.out.println(entry.getKey() + ": " + entry.getValue()); 20}
Output:
Alice: 92
Bob: 78

Map does not implement Iterable. You must iterate one of its views: entrySet(), keySet(), or values(). A mistake that appears often in fresher pull requests is trying to iterate a Map directly in a for-each loop — the compiler catches it, but knowing why it fails saves time.

Mistake 3 — Tracking Index Manually Inside for-each

Java
1import java.util.List; 2 3public class ManualIndexDemo { 4 5 public static void main(String[] args) { 6 7 List<String> items = List.of("Laptop", "Mouse", "Keyboard"); 8 9 // Anti-pattern — manually counting inside for-each 10 int index = 0; 11 for (String item : items) { 12 System.out.println(index + ". " + item); 13 index++; 14 } 15 } 16}
Output:
0. Laptop
1. Mouse
2. Keyboard

This works but signals the wrong tool was chosen. When the index is needed, the basic for loop with i provides it naturally and eliminates the manual variable. Teams consistently flag the manual counter pattern as a code smell in reviews.

Mistake 4 — Assuming for-each Preserves Insertion Order for All Collections

A HashSet does not guarantee any particular iteration order. Beginners sometimes use for-each on a HashSet and expect elements to appear in the order they were added — they do not. Use LinkedHashSet when insertion order matters, or TreeSet for sorted order.

Interview Questions

Q1. What is the Java for-each loop and how does it differ from the basic for loop?

The for-each loop, introduced in Java 5, iterates over every element in an array or any Iterable without exposing an index counter. The basic for loop uses an explicit counter and is required when position matters, when iterating backwards, or when modifying the collection during iteration. For-each is the preferred choice for simple read-only traversal because it eliminates the off-by-one error class entirely and produces cleaner, more readable code.

Q2. What does the Java compiler generate internally for a for-each loop?

For arrays, the compiler generates a basic indexed for loop — for (int i = 0; i < array.length; i++) — with the array element accessed at each index. For classes that implement Iterable, the compiler generates an Iterator-based loop using collection.iterator(), hasNext(), and next(). This is why for-each over a List cannot be used to remove elements directly — the generated Iterator tracks the collection's modification count and throws ConcurrentModificationException if it detects direct modification.

Q3. Why does for-each throw ConcurrentModificationException when removing from a List?

The for-each loop over a List uses an Iterator internally. The Iterator holds a snapshot of the List's modification count at creation time. When you call list.remove() directly while the iterator is active, the list's modification count changes. On the next hasNext() or next() call, the iterator detects the mismatch and throws ConcurrentModificationException. The safe alternatives are Iterator.remove(), List.removeIf(), or collecting items to remove and calling removeAll() after the loop.

Q4. What types support the Java for-each loop?

Any array type and any class that implements java.lang.Iterable. All Java Collections Framework types — ArrayList, LinkedList, HashSet, TreeSet, ArrayDeque, and their interfaces — implement Iterable. Map does not implement Iterable directly; you must iterate one of its views (entrySet(), keySet(), or values()). Primitive types and non-iterable objects produce a compile-time error in a for-each loop.

Q5. When would you use a basic for loop instead of for-each?

Use the basic for loop when you need the current index for business logic, when iterating backwards, when using a step size other than 1, when you need to modify the collection safely during iteration using an explicit index, or when iterating multiple arrays simultaneously using a shared counter. Use for-each for everything else — any simple read-only full pass over a collection or array where position is irrelevant.

Q6. Can for-each be used with a Map in Java?

Not directly. Map does not implement Iterable. To use for-each with a Map, iterate one of its collection views: map.entrySet() for key-value pairs, map.keySet() for keys only, or map.values() for values only. Each of these returns a Collection that implements Iterable and can be used in a for-each loop normally.

FAQs

What is the for-each loop in Java?

The for-each loop, also called the enhanced for loop, iterates over every element in an array or any class that implements Iterable without requiring an explicit index counter. Introduced in Java 5, it is the preferred choice for simple read-only traversal of arrays and collections.

Does the Java for-each loop give you the index?

No. The for-each loop provides only the current element — there is no index variable available. If you need the index, use a basic for loop, or in Java 8+ use IntStream.range() to generate indices while streaming elements.

Can you use for-each with a Map in Java?

Not directly, because Map does not implement Iterable. Use map.entrySet() for key-value pairs, map.keySet() for keys only, or map.values() for values only — each of these collection views is iterable with for-each.

Why does removing from a List inside a for-each loop throw an exception?

The for-each loop uses an Iterator internally, which tracks the collection's modification count. Calling list.remove() directly changes the count. The iterator detects the mismatch on the next call and throws ConcurrentModificationException. Use list.removeIf() or Iterator.remove() for safe removal during iteration.

Is the Java for-each loop slower than the basic for loop?

For arrays, performance is identical — the compiler generates the same bytecode. For collections, the for-each loop uses an Iterator, which adds a method call per element compared to list.get(i). In practice, this difference is negligible for typical application workloads. Choose the loop form based on readability and correctness, not micro-optimization.

Can you break out of a for-each loop in Java?

Yes. break exits the for-each loop immediately, just like any other loop. continue skips the rest of the current iteration and moves to the next element. return exits the enclosing method entirely.

Summary

The for-each loop exists to make read-only iteration over arrays and collections cleaner, shorter, and immune to off-by-one bugs. The tradeoff is deliberate: you give up index access, reverse iteration, and safe in-place modification in exchange for code that is simpler to write and harder to get wrong.

Internally, it compiles to a basic indexed loop for arrays and an Iterator-based loop for collections — which is exactly why modifying a List directly during for-each iteration throws ConcurrentModificationException. Knowing what is generated under the hood is what interviewers probe when they ask about this loop.

For interviews, be ready to explain the two internal compilation paths, demonstrate why ConcurrentModificationException occurs with a concrete example, and know when to reach for a basic for loop instead. Those three points cover the depth that both service-based and product-based companies test on this topic.

What to Read Next

TopicLink
How the basic for loop works and when the index counter is neededJava for Loop →
How Java Iterator works internally and when to use it directlyJava Iterator →
How the ArrayList stores and retrieves elements internallyJava ArrayList →
How break and continue control iteration flow inside loopsJava break and continue →
How the Java Stream API replaces for-each for transformation and filteringJava Streams API →
Java for-each Loop | DevStackFlow