Java Iterator
Every collection you have met, ArrayList, LinkedList, HashSet and the key sets of a HashMap, can give you an iterator: an object that walks through the items one at a time. The for-each loop uses an iterator behind the scenes. Using one directly gives you one extra power: removing items safely while you walk.
hasNext and next
iterator() returns an Iterator. hasNext() tells whether there is another item, and next() returns it and moves on:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<String> colours = new ArrayList<>(List.of("red", "green", "blue"));
Iterator<String> it = colours.iterator();
while (it.hasNext()) {
String c = it.next();
System.out.println(c.toUpperCase());
}
}
}RED GREEN BLUE
This loop does the same as for (String c : colours). In fact, the for-each loop works with any object that can give an iterator, which is why it works with every collection.
Removing while looping
Removing items from a list inside a for-each loop over that list confuses its hidden iterator, and Java stops with an exception:
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(List.of(4, 7, 10, 13));
for (int n : numbers) {
if (n % 2 == 0) {
numbers.remove(Integer.valueOf(n));
}
}
System.out.println(numbers);
}
}Exception in thread "main" java.util.ConcurrentModificationException at java.base/java.util.ArrayList$Itr.checkForComodification(Unknown Source) at java.base/java.util.ArrayList$Itr.next(Unknown Source) at Main.main(Main.java:7)
The safe way is to remove through the iterator itself, with its remove() method, which removes the item that next() returned last:
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>(List.of(4, 7, 10, 13, 16));
Iterator<Integer> it = numbers.iterator();
while (it.hasNext()) {
if (it.next() % 2 == 0) {
it.remove();
}
}
System.out.println(numbers);
}
}[7, 13]
For this common case, numbers.removeIf(n -> n % 2 == 0) is even shorter; it uses a lambda.
Iterating over a map
A map has no iterator of its own, but its keySet(), values() and entrySet() do. Removing through the entry set's iterator removes the pair from the map:
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
public class Main {
public static void main(String[] args) {
Map<String, Integer> stock = new TreeMap<>(Map.of("pen", 0, "book", 4, "ink", 0, "bag", 2));
Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, Integer> e = it.next();
if (e.getValue() == 0) {
System.out.println("Sold out: " + e.getKey());
it.remove();
}
}
System.out.println(stock);
}
}Sold out: ink
Sold out: pen
{bag=2, book=4}ListIterator: both directions and changes
Lists offer a ListIterator, which can also go backwards and replace the current item with set:
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
public class Main {
public static void main(String[] args) {
List<String> names = new ArrayList<>(List.of("asha", "ben", "chitra"));
ListIterator<String> it = names.listIterator();
while (it.hasNext()) {
String n = it.next();
it.set(n.substring(0, 1).toUpperCase() + n.substring(1));
}
System.out.println(names);
while (it.hasPrevious()) {
System.out.print(it.previous() + " ");
}
System.out.println();
}
}[Asha, Ben, Chitra] Chitra Ben Asha
Practice exercises
- Use an iterator to print every second item of a list.
- Remove all words shorter than 4 letters from a list with an iterator.
- Remove all entries with a value below 50 from a map of names and marks.