Question
Given the following Java code:
List<String> someList = new ArrayList<>();
someList.add("monkey");
someList.add("donkey");
someList.add("skeleton key");
for (String item : someList) {
System.out.println(item);
}
What is the equivalent loop written without Java's enhanced for-each syntax?
Also, why do beginners often run into problems when they try to modify the original collection by assigning to the loop variable inside a for-each loop?
Short Answer
By the end of this page, you will understand how Java's enhanced for-each loop works, what code it is roughly equivalent to under the hood, why assigning to the loop variable does not update the original list, and when to use a regular for loop or an Iterator instead.
Concept
Java's enhanced for-each loop is a simpler way to iterate through arrays and collections.
Instead of manually managing an index or an Iterator, you write:
for (String item : someList) {
System.out.println(item);
}
This means:
- take each element from
someList - store it temporarily in
item - run the loop body
- repeat until there are no more elements
For a List, the loop is conceptually similar to using an Iterator:
for (Iterator<String> it = someList.iterator(); it.hasNext(); ) {
String item = it.next();
System.out.println(item);
}
For an array, it behaves more like stepping through indexes from 0 to length - 1.
Why this matters:
- It makes code shorter and easier to read.
- It reduces mistakes involving indexes.
- It is ideal when you only need to read values.
But it also has an important limitation:
- the loop variable is just a local variable for the current element
- reassigning that variable does not replace the element inside the collection
So this:
for (String item : someList) {
item = item.toUpperCase();
}
does not update the list contents. It only changes what the local variable item refers to during that iteration.
Mental Model
Think of a for-each loop like reading names from a printed attendance sheet.
A helper points at each name one at a time and says, "Here is the current one." You can read the name, print it, or use it in calculations.
But if you write a different name on your own notepad, you have not changed the attendance sheet itself.
In this analogy:
- the collection is the attendance sheet
- the loop variable is the name currently handed to you
- assigning a new value to the loop variable is like changing your note, not the original sheet
If you actually want to update the original list, you need a way to write back into the list, such as:
- using an index with
list.set(index, value) - using a
ListIterator - replacing the whole collection content in another way
Syntax and Examples
Basic syntax
for (Type variable : collectionOrArray) {
// use variable
}
Example with a list:
List<String> animals = new ArrayList<>();
animals.add("monkey");
animals.add("donkey");
animals.add("skeleton key");
for (String animal : animals) {
System.out.println(animal);
}
This prints:
monkey
donkey
skeleton key
Equivalent form using an Iterator
For most collections, the enhanced for-each loop is roughly equivalent to:
for (Iterator<String> it = animals.iterator(); it.hasNext(); ) {
String animal = it.next();
System.out.println(animal);
}
Equivalent form using an index
Because this example uses a List, you could also write:
for (int i = 0; i < animals.size(); i++) {
String animal = animals.get(i);
System.out.println(animal);
}
This is not exactly how for-each is implemented for every collection, but it produces the same result for a List.
Example of a common misunderstanding
for (String animal : animals) {
animal = animal.toUpperCase();
}
System.out.println(animals);
Output:
[monkey, donkey, skeleton key]
Why? Because animal is only a local variable. Reassigning it does not replace the element in the list.
Correct way to update list elements
for (int i = 0; i < animals.size(); i++) {
animals.set(i, animals.get(i).toUpperCase());
}
System.out.println(animals);
Output:
[MONKEY, DONKEY, SKELETON KEY]
Step by Step Execution
Consider this code:
List<String> items = List.of("monkey", "donkey", "skeleton key");
for (String item : items) {
System.out.println(item);
}
You can think of it as if Java does this:
Iterator<String> it = items.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println(item);
}
Step by step:
items.iterator()creates an iterator positioned before the first element.it.hasNext()checks whether another element exists.it.next()returns the next element, such as"monkey".- That value is assigned to the loop variable
item. System.out.println(item);prints the current value.- The loop repeats for
"donkey". - The loop repeats for
"skeleton key". - When
hasNext()returnsfalse, the loop stops.
Now look at this variation:
List<String> items = new ArrayList<>();
items.add("monkey");
items.add("donkey");
for (String item : items) {
item = item.toUpperCase();
}
System.out.println(items);
Step by step:
- First iteration:
itemgets"monkey". item = item.toUpperCase();changes the local variableitemto refer to"MONKEY".- The list still contains
"monkey"because nothing wrote back toitems. - Second iteration:
itemgets"donkey". itembecomes"DONKEY"locally.- The list still contains
"donkey". - Printing
itemsshows the original values unchanged.
Real World Use Cases
Enhanced for-each loops are common when you want to read each element in a sequence without worrying about indexes.
Common uses
- Printing log messages from a list
- Validating input values one by one
- Summing numbers in an array or collection
- Rendering UI data such as product names or menu items
- Processing API results returned as lists
Example: validating user input
List<String> usernames = List.of("alice", "", "charlie");
for (String username : usernames) {
if (username.isBlank()) {
System.out.println("Invalid username found");
}
}
Example: summing values
int[] numbers = {10, 20, 30};
int total = 0;
for (int number : numbers) {
total += number;
}
System.out.println(total); // 60
When not to use it
Avoid for-each when you need to:
- know the current index
- replace elements in a list
- remove elements during iteration without proper iterator methods
- skip around manually in the collection
Real Codebase Usage
In real Java projects, developers use enhanced for-each loops mainly for read-only traversal.
Common patterns
1. Read and process each item
for (Order order : orders) {
sendConfirmationEmail(order);
}
2. Validation with guard clauses
for (String email : emails) {
if (email == null || email.isBlank()) {
throw new IllegalArgumentException("Email cannot be empty");
}
}
3. Aggregation
double total = 0;
for (Invoice invoice : invoices) {
total += invoice.getAmount();
}
When developers switch to other tools
Use an index-based loop when replacing list elements
for (int i = 0; i < names.size(); i++) {
names.set(i, names.get(i).trim());
}
Use an Iterator when removing safely during iteration
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().isBlank()) {
it.remove();
}
}
Use streams for transformations
List<String> upper = names.stream()
.map(String::toUpperCase)
.toList();
In production code, choosing the loop style depends on the task:
- read only ->
for-each - update by position -> index loop
- remove while iterating ->
Iterator - build transformed data -> stream operations
Common Mistakes
1. Thinking assignment updates the collection
Broken code:
for (String item : items) {
item = "changed";
}
Problem:
- This only changes the local loop variable.
- The list stays the same.
Fix:
for (int i = 0; i < items.size(); i++) {
items.set(i, "changed");
}
2. Removing elements directly from the collection during for-each
Broken code:
for (String item : items) {
if (item.isBlank()) {
items.remove(item);
}
}
Problem:
- This can cause
ConcurrentModificationException.
Fix:
Iterator<String> it = items.iterator();
while (it.hasNext()) {
if (it.next().isBlank()) {
it.remove();
}
}
3. Expecting an index inside for-each
Broken thinking:
for-eachgives you each value, not its numeric position.
Fix:
Use a regular loop if you need the index:
for (int i = 0; i < items.size(); i++) {
System.out.println(i + ": " + items.get(i));
}
4. Confusing object mutation with variable reassignment
This code can work:
for (StringBuilder sb : builders) {
sb.append("!");
}
Why?
- You are mutating the object that
sbrefers to. - You are not reassigning
sbitself.
This code does not replace list elements:
for (StringBuilder sb : builders) {
sb = new StringBuilder("new");
}
Why?
- Now you are only changing the local variable.
5. Modifying immutable collections
Broken code:
List<String> items = List.of("a", "b");
items.set(0, "x");
Problem:
List.of(...)creates an immutable list.
Fix:
List<String> items = new ArrayList<>(List.of("a", "b"));
items.set(0, "x");
Comparisons
| Approach | Best for | Can access index? | Can replace elements easily? | Can remove safely while iterating? |
|---|---|---|---|---|
Enhanced for-each | Reading elements | No | No | No |
Index-based for loop | Lists and arrays when position matters | Yes | Yes | Sometimes, but must be careful |
Iterator | Safe traversal with removal | No | Not directly | Yes, with remove() |
ListIterator | Traversing and updating lists | Partial | Yes | Yes |
Enhanced for-each vs regular for
for (String item : items) {
System.out.println(item);
}
- simpler
- more readable
- no index management
for (int i = 0; i < items.size(); i++) {
System.out.println(items.get(i));
}
- useful when you need
i - useful for
set(i, value)
for-each vs Iterator
for (String item : items) {
System.out.println(item);
}
- best for simple reads
Iterator<String> it = items.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.isBlank()) {
it.remove();
}
}
- best when removing during iteration
- exposes iteration mechanics directly
Cheat Sheet
Enhanced for-each syntax
for (Type variable : collectionOrArray) {
// use variable
}
Equivalent for a collection
for (Iterator<Type> it = collection.iterator(); it.hasNext(); ) {
Type variable = it.next();
// use variable
}
Equivalent for a list if you want indexes
for (int i = 0; i < list.size(); i++) {
Type variable = list.get(i);
}
Key rules
- Works with arrays and
Iterablecollections. - The loop variable is a local variable for the current element.
- Reassigning the loop variable does not update the collection.
- Use
list.set(i, value)to replace elements. - Use
Iterator.remove()to remove safely while iterating.
Remember
for (String s : list) {
s = s.toUpperCase(); // does not change list
}
for (int i = 0; i < list.size(); i++) {
list.set(i, list.get(i).toUpperCase()); // changes list
}
Edge cases
List.of(...)is immutable.- Modifying the collection structure during
for-eachusually causes errors. - Mutating the object itself is different from reassigning the loop variable.
FAQ
What is Java's enhanced for loop?
It is a shorter syntax for iterating through arrays and collections without manually writing an index or iterator.
Is a for-each loop the same as a normal for loop?
Not exactly. It can produce the same result, but for collections it is conceptually based on an Iterator, not always an index.
Why doesn't assigning to the loop variable change the list?
Because the loop variable is only a temporary local variable holding the current element reference or value.
Can I remove items inside a Java for-each loop?
Not safely by calling list.remove(...) directly. Use an Iterator and its remove() method.
Can I modify objects inside a for-each loop?
Yes. If the element is a mutable object, you can change the object's internal state. What you cannot do is replace the collection element just by reassigning the loop variable.
When should I use a regular for loop instead?
Use a regular for loop when you need the index or need to replace elements in a list.
Does for-each work on arrays too?
Yes. It works on both arrays and collections that implement Iterable.
Mini Project
Description
Build a small Java program that displays a list of task names, then creates an updated version of those tasks in uppercase. This project demonstrates the difference between reading with for-each and actually changing list data with an index-based loop.
Goal
Learn when for-each is appropriate and when you must use a different loop to update list contents.
Requirements
Create a list of at least three task names.
Print all tasks using an enhanced for-each loop.
Attempt to uppercase tasks by assigning to the for-each loop variable.
Show that the original list is unchanged.
Use an index-based loop to update the list correctly.
Print the final updated list.
Keep learning
Related questions
Add External JAR Files to an IntelliJ IDEA Java Project
Learn how to add external JAR dependencies to an IntelliJ IDEA Java project using module libraries, and when to use Maven or Gradle instead.
Avoiding Java Code in JSP with JSP 2: EL and JSTL Explained
Learn how to avoid Java scriptlets in JSP 2 using Expression Language and JSTL, with examples, best practices, and common mistakes.
Call a Method After a Delay in Android Java
Learn how to run Java code after a delay in Android using Handler.postDelayed, manage the main thread, and cancel callbacks safely.