Skip to content
Dev Dump

Collection Iteration

“Iteration is fundamental to working with collections. Understanding the different iteration mechanisms and their trade-offs is essential for writing efficient and maintainable code.”

Collection iteration is the process of accessing and processing each element in a collection sequentially. Java provides multiple iteration mechanisms, each with specific use cases, performance characteristics, and capabilities.

  • Traditional loops: Index-based iteration (for arrays and lists)
  • Iterator pattern: Generic iteration for all collections
  • Enhanced for-loop: Simplified syntax for Iterables
  • Stream API: Functional programming approach
  • forEach method: Imperative style with lambdas
Iterable<T> (interface)
├── Collection<T> (interface)
│ ├── List<T>, Set<T>, Queue<T>...
│ └── All collection implementations
├── Map<K,V> (not directly iterable)
│ └── entrySet(), keySet(), values() return iterables
└── Custom iterable classes
Iterator<T> (interface)
├── ListIterator<T> (interface) - Bidirectional iteration
├── Spliterator<T> (interface) - Parallel iteration support
└── Various implementation classes
public class IterableExample {
public void demonstrateIterable() {
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
// Using iterator() method
Iterator<String> iterator = fruits.iterator();
while (iterator.hasNext()) {
String fruit = iterator.next();
System.out.println(fruit);
}
// Using enhanced for-loop (syntax sugar for iterator)
for (String fruit : fruits) {
System.out.println(fruit);
}
// Using forEach method (Java 8+)
fruits.forEach(System.out::println);
}
}
public class IteratorBestPractices {
// ✅ Good - Safe element removal during iteration
public void removeEvenNumbers(List<Integer> numbers) {
Iterator<Integer> iterator = numbers.iterator();
while (iterator.hasNext()) {
Integer number = iterator.next();
if (number % 2 == 0) {
iterator.remove(); // Safe removal
}
}
}
// ❌ Bad - ConcurrentModificationException risk
public void removeEvenNumbersBad(List<Integer> numbers) {
for (Integer number : numbers) {
if (number % 2 == 0) {
numbers.remove(number); // Throws exception!
}
}
}
// ✅ Good - Check hasNext() before next()
public void safeIteration(Collection<String> items) {
Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
processItem(item);
}
}
}

ListIterator extends Iterator and provides bidirectional iteration capabilities for List implementations.

public class ListIteratorExample {
public void demonstrateListIterator() {
List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c", "d"));
ListIterator<String> iterator = items.listIterator();
// Forward iteration
System.out.println("Forward iteration:");
while (iterator.hasNext()) {
String item = iterator.next();
System.out.println("Next: " + item);
}
// Backward iteration
System.out.println("\nBackward iteration:");
while (iterator.hasPrevious()) {
String item = iterator.previous();
System.out.println("Previous: " + item);
}
// Adding elements during iteration
ListIterator<String> addIterator = items.listIterator();
while (addIterator.hasNext()) {
String item = addIterator.next();
if (item.equals("b")) {
addIterator.add("b1"); // Add after current element
}
}
// Modifying elements during iteration
ListIterator<String> modifyIterator = items.listIterator();
while (modifyIterator.hasNext()) {
String item = modifyIterator.next();
if (item.equals("c")) {
modifyIterator.set("c1"); // Replace current element
}
}
}
}

The enhanced for-loop is syntactic sugar that internally uses an Iterator.

public class EnhancedForLoopExample {
public void demonstrateEnhancedForLoop() {
List<String> fruits = Arrays.asList("apple", "banana", "cherry");
// Basic usage
for (String fruit : fruits) {
System.out.println(fruit);
}
// With arrays
String[] colors = {"red", "green", "blue"};
for (String color : colors) {
System.out.println(color);
}
// With sets
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
for (Integer number : numbers) {
System.out.println(number);
}
// With maps (iterating over entrySet)
Map<String, Integer> scores = Map.of("Alice", 95, "Bob", 87, "Charlie", 92);
for (Map.Entry<String, Integer> entry : scores.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
}
}

The Stream API provides a functional approach to collection processing.

public class StreamIterationExample {
public void demonstrateStreamAPI() {
List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David", "Eve");
// Basic iteration
names.stream().forEach(System.out::println);
// Filtering and processing
names.stream()
.filter(name -> name.length() > 4)
.map(String::toUpperCase)
.forEach(System.out::println);
// Collecting results
List<String> longNames = names.stream()
.filter(name -> name.length() > 4)
.collect(Collectors.toList());
// Parallel processing
names.parallelStream()
.forEach(System.out::println);
// Finding elements
Optional<String> firstLongName = names.stream()
.filter(name -> name.length() > 4)
.findFirst();
// Counting elements
long count = names.stream()
.filter(name -> name.length() > 4)
.count();
}
}

The forEach method provides a simple way to iterate with lambda expressions.

public class ForEachExample {
public void demonstrateForEach() {
List<String> items = Arrays.asList("item1", "item2", "item3");
// Basic forEach
items.forEach(item -> System.out.println(item));
// Method reference
items.forEach(System.out::println);
// With index (using IntStream)
IntStream.range(0, items.size())
.forEach(i -> System.out.println(i + ": " + items.get(i)));
// With maps
Map<String, Integer> scores = Map.of("Alice", 95, "Bob", 87);
scores.forEach((name, score) ->
System.out.println(name + " scored " + score));
}
}

For List implementations that support random access, index-based iteration can be efficient.

public class IndexBasedIterationExample {
public void demonstrateIndexBasedIteration() {
List<String> items = Arrays.asList("a", "b", "c", "d", "e");
// Forward iteration
for (int i = 0; i < items.size(); i++) {
String item = items.get(i);
System.out.println("Index " + i + ": " + item);
}
// Backward iteration
for (int i = items.size() - 1; i >= 0; i--) {
String item = items.get(i);
System.out.println("Index " + i + ": " + item);
}
// Skipping elements
for (int i = 0; i < items.size(); i += 2) {
String item = items.get(i);
System.out.println("Even index " + i + ": " + item);
}
}
}
Iteration MethodPerformanceMemorySafetyUse Case
IteratorGoodLowHighSafe removal, generic
Enhanced For-LoopGoodLowHighSimple iteration
ListIteratorGoodLowHighBidirectional, modification
Stream APIVariableMediumHighFunctional processing
forEachGoodLowHighLambda expressions
Index-basedExcellent*LowMediumRandom access lists

* Only for ArrayList and other random-access implementations

  1. Use Iterator for safe removal during iteration
  2. Use enhanced for-loop for simple iteration
  3. Use Stream API for complex processing and filtering
  4. Use index-based iteration only for ArrayList and similar
  5. Avoid modifying collections during enhanced for-loop iteration
// ❌ This will throw ConcurrentModificationException
List<String> items = new ArrayList<>(Arrays.asList("a", "b", "c"));
for (String item : items) {
if (item.equals("b")) {
items.remove(item); // Exception!
}
}
// ✅ Use Iterator for safe removal
Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if (item.equals("b")) {
iterator.remove(); // Safe!
}
}
// ❌ Potential infinite loop with Iterator
Iterator<String> iterator = items.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
items.add("new item"); // Never reaches end!
}
// ✅ Be careful with collection modifications during iteration
public class CustomIteratorExample {
public static class RangeIterator implements Iterator<Integer> {
private int current;
private final int end;
public RangeIterator(int start, int end) {
this.current = start;
this.end = end;
}
@Override
public boolean hasNext() {
return current < end;
}
@Override
public Integer next() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return current++;
}
}
public void demonstrateCustomIterator() {
Iterator<Integer> rangeIterator = new RangeIterator(1, 6);
while (rangeIterator.hasNext()) {
System.out.println(rangeIterator.next());
}
}
}
public class SpliteratorExample {
public void demonstrateSpliterator() {
List<String> items = Arrays.asList("a", "b", "c", "d", "e", "f");
Spliterator<String> spliterator = items.spliterator();
// Try to split for parallel processing
Spliterator<String> secondHalf = spliterator.trySplit();
// Process first half
spliterator.forEachRemaining(item ->
System.out.println("First half: " + item));
// Process second half
secondHalf.forEachRemaining(item ->
System.out.println("Second half: " + item));
}
}

Java provides multiple iteration mechanisms, each suited for different scenarios:

  • Iterator: Best for safe modification during iteration
  • Enhanced For-Loop: Simplest syntax for basic iteration
  • ListIterator: Bidirectional iteration with modification capabilities
  • Stream API: Functional programming approach for complex processing
  • forEach: Simple lambda-based iteration
  • Index-based: Best performance for random-access lists

Choose the appropriate iteration method based on your specific requirements for performance, safety, and functionality.