Collection Iteration
Java provides several ways to iterate over collections. Each has its own strengths — pick the one that fits your use case.
Quick Comparison
Section titled “Quick Comparison”| Method | Best For | Can Remove? | Functional? |
|---|---|---|---|
| Enhanced for-loop | Simple iteration | No | No |
| Iterator | Safe removal during iteration | Yes | No |
| ListIterator | Bidirectional traversal + modification | Yes (+ add/set) | No |
| forEach | Clean one-liners with lambdas | No | Partially |
| Stream API | Filter/map/reduce pipelines | No | Yes |
| Index-based for | When you need the index | Manual | No |
Enhanced For-Loop
Section titled “Enhanced For-Loop”Syntactic sugar over Iterator. Cleanest syntax for simple read-only traversal.
for (String fruit : fruits) { System.out.println(fruit);}
// Works with arrays, Lists, Sets — anything Iterablefor (Map.Entry<String, Integer> e : map.entrySet()) { System.out.println(e.getKey() + ": " + e.getValue());}Iterator
Section titled “Iterator”Use when you need to remove elements safely during iteration.
Iterator<String> it = list.iterator();while (it.hasNext()) { String item = it.next(); if (shouldRemove(item)) { it.remove(); // safe — this is the only way to remove during iteration }}ConcurrentModificationException — the most common pitfall:
// WRONG — throws ConcurrentModificationExceptionfor (String item : list) { if (item.equals("x")) list.remove(item);}
// RIGHT — use Iterator.remove()Iterator<String> it = list.iterator();while (it.hasNext()) { if (it.next().equals("x")) it.remove();}ListIterator
Section titled “ListIterator”Extends Iterator for List types. Supports bidirectional traversal and in-place modification.
ListIterator<String> it = list.listIterator();
// Forwardwhile (it.hasNext()) { String item = it.next(); if (item.equals("old")) it.set("new"); // replace current}
// Backwardwhile (it.hasPrevious()) { System.out.println(it.previous());}
// Insert after current positionit.add("inserted");forEach + Lambdas
Section titled “forEach + Lambdas”Concise for simple operations. Available on all Iterable types and on Map.
list.forEach(System.out::println);list.forEach(item -> process(item));
// Map has its own two-arg forEachmap.forEach((key, value) -> System.out.println(key + "=" + value));Stream API
Section titled “Stream API”Best for complex processing pipelines — filter, transform, aggregate.
List<String> result = names.stream() .filter(name -> name.length() > 4) .map(String::toUpperCase) .sorted() .collect(Collectors.toList());
long count = names.stream() .filter(n -> n.startsWith("A")) .count();
Optional<String> first = names.stream() .filter(n -> n.length() > 4) .findFirst();
// Parallel processing (use with care)names.parallelStream().forEach(System.out::println);Index-Based For Loop
Section titled “Index-Based For Loop”Only efficient for random-access lists like ArrayList. Avoid on LinkedList (each get(i) is O(n)).
for (int i = 0; i < list.size(); i++) { System.out.println(i + ": " + list.get(i));}
// Useful when you need the index or need to skip elementsfor (int i = list.size() - 1; i >= 0; i--) { if (shouldRemove(list.get(i))) list.remove(i);}Fail-Fast vs Fail-Safe Iterators
Section titled “Fail-Fast vs Fail-Safe Iterators”This is why ConcurrentModificationException happens — and when it doesn’t.
Fail-Fast (default behavior)
Section titled “Fail-Fast (default behavior)”Most collection iterators (ArrayList, HashMap, HashSet, etc.) are fail-fast. They track a modification count (modCount) internally. If the collection is modified structurally (add/remove) after the iterator is created — by anything other than the iterator itself — it immediately throws ConcurrentModificationException.
List<String> list = new ArrayList<>(List.of("a", "b", "c"));Iterator<String> it = list.iterator();list.add("d"); // modCount changesit.next(); // throws ConcurrentModificationExceptionThis is a best-effort detection — it’s not guaranteed in concurrent scenarios, but it catches most bugs fast.
Fail-Safe (concurrent collections)
Section titled “Fail-Safe (concurrent collections)”Iterators from java.util.concurrent collections (ConcurrentHashMap, CopyOnWriteArrayList, etc.) are fail-safe. They never throw ConcurrentModificationException because they work on a separate copy or a snapshot of the data.
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>(List.of("a", "b", "c"));Iterator<String> it = list.iterator();list.add("d"); // no problem — iterator works on a snapshotit.next(); // "a" — from the original snapshotTrade-off: fail-safe iterators may not reflect the latest modifications.
Quick Reference
Section titled “Quick Reference”| Fail-Fast | Fail-Safe | |
|---|---|---|
| Collections | ArrayList, HashMap, HashSet, etc. | ConcurrentHashMap, CopyOnWriteArrayList |
| On modification | Throws ConcurrentModificationException | Keeps going (snapshot) |
| Performance | No overhead | Copy/snapshot overhead |
| Reflects changes? | N/A (it throws) | No — works on stale data |
Rules of Thumb
Section titled “Rules of Thumb”- Default to enhanced for-loop — simplest and readable
- Need to remove? — use
Iterator - Need index? — index-based loop (only on
ArrayList) - Processing pipeline? — Stream API
- Never modify a collection inside an enhanced for-loop
- Multi-threaded iteration? — use concurrent collections (fail-safe iterators)