Iterator¶
When using collections such as List
orSet
, we often want to perform certain operations (other than add or remove) on all of their items. Such collections store data in various ways, e.g. in an array. However, in order not to break the SOLID rules, we should not give users the possibility to modify it directly.
To provide access to the elements of the collection, we use the Iterator
pattern, which enables iteration over the collection. Such an iterator should be able to:
- get the next item
- check, if there is a next item
Examples¶
When creating an object that we want to iterate over, we have the following options:
- we can use the
Iterable
interface which returns an iterator. This interface is useful when you use existing collections that give access to existing iterators (e.g., using theiterator()
method). - we can implement the
Iterator
interface and write implementations of thehasNext
andnext
methods.
The following example uses the Iterable
interface (i.e. does not implement the pattern directly):
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class JavaFile implements Iterable<String> {
private String fileName;
private String className;
private List<String> linesContent = new ArrayList<>();
public void addLine(final String line) {
linesContent.add(line);
}
@Override
public String toString() {
return "JavaFile{" +
"fileName='" + fileName + '\'' +
", className='" + className + '\'' +
", linesContent=" + String.join("\n", linesContent) +
'}';
}
@Override
public Iterator<String> iterator() {
return linesContent.iterator();
}
}
The next example uses both of these interfaces:
- the
ParkingLot
class implements theIterable
interface and has the ability to getCarIterator
, which allows you to sequentially fetchCar
objects from an array inside theParkingLot
class - the
CarIterator
class implements theIterator
interface and has the ability to get another element.
public interface Car {
String getVehicleInfo();
}
import lombok.ToString;
@ToString
public class SimpleCar implements Car {
private static int index = 0;
private final String info;
public SimpleCar() {
info = "Mazda 6 with id " + ++index;
}
@Override
public String getVehicleInfo() {
return info;
}
}
import java.util.Iterator;
public class ParkingLot implements Iterable<Car> {
private static final int INITIAL_CAPACITY = 5;
private int indexToAdd = 0;
private Car[] cars = new Car[INITIAL_CAPACITY];
public void add(final Car car) {
if (indexToAdd == cars.length) {
Car[] biggerCars = new Car[2 * cars.length];
for (int idx = 0; idx < cars.length; idx++) {
biggerCars[idx] = cars[idx];
}
cars = biggerCars;
} else {
cars[indexToAdd++] = car;
}
}
@Override
public Iterator<Car> iterator() {
return new CarIterator();
}
public class CarIterator implements Iterator<Car> {
private int index = 0;
@Override
public boolean hasNext() {
return index < cars.length && cars[index] != null;
}
@Override
public Car next() {
return cars[index++];
}
}
}
import java.util.Iterator;
public class ParkingLotUsage {
public static void main(String[] args) {
final ParkingLot parkingLot = new ParkingLot();
for (int idx = 0; idx < 12; idx++) {
parkingLot.add(new SimpleCar());
}
final Iterator<Car> iterator = parkingLot.iterator();
while (iterator.hasNext()) {
final Car car = iterator.next();
System.out.println(car);
}
}
}
/* Output:
SimpleCar(info=Mazda 6 with id 1)
SimpleCar(info=Mazda 6 with id 2)
SimpleCar(info=Mazda 6 with id 3)
SimpleCar(info=Mazda 6 with id 4)
SimpleCar(info=Mazda 6 with id 5)
SimpleCar(info=Mazda 6 with id 7)
SimpleCar(info=Mazda 6 with id 8)
SimpleCar(info=Mazda 6 with id 9)
SimpleCar(info=Mazda 6 with id 10)
SimpleCar(info=Mazda 6 with id 11)
*/
The Iterator pattern is useful when we need to implement our own collections (which, contrary to popular belief, is not a rare case) and be able to perform operations on their elements. For this purpose, we usually use the already existing Iterable
and Iterator
interfaces.