Java Iterable Interface
Java Iterable Interface
Every time you write a for-each loop over an ArrayList, a HashSet, or a TreeMap's key set, one interface makes it work: Iterable. It is the single contract that connects your data structure to Java's for-each syntax. Without it, the Java compiler refuses to compile the loop.
Understanding Iterable is more than knowing a definition. It explains why every collection in Java supports for-each, how you can write your own class that also supports it, and what the compiler literally does when it sees for (String s : myList).
What Is the Iterable Interface?
java.lang.Iterable<T> is an interface in the java.lang package, which means it is automatically imported — you never need to write an import statement for it. It declares exactly one abstract method:
public interface Iterable<T> {
Iterator<T> iterator(); ← the one required method
// Java 8+ default methods (optional to override):
default void forEach(Consumer<? super T> action) { ... }
default Spliterator<T> spliterator() { ... }
}
Any class that implements Iterable<T> can be used in a Java enhanced for-each loop. That is the entire purpose of the interface: it is the bridge between a data structure and the for (T item : collection) syntax.
The diagram below shows where Iterable sits in the Collections hierarchy and what extends from it.
ITERABLE IN THE COLLECTIONS HIERARCHY
java.lang.Iterable<T> ← root of traversal contract
│
└── java.util.Collection<T> ← root of single-element collections
│
├── java.util.List<T> → ArrayList, LinkedList, Vector
├── java.util.Set<T> → HashSet, TreeSet, LinkedHashSet
└── java.util.Queue<T> → ArrayDeque, PriorityQueue, LinkedList
java.util.Map<K,V> does NOT extend Iterable directly.
Its views do: map.keySet() → Set (Iterable), map.values() → Collection (Iterable)
KEY POINT:
Any class that implements Iterable<T> can be used in for-each:
for (T item : myObject) { ... }
The compiler translates this to:
Iterator<T> it = myObject.iterator();
while (it.hasNext()) {
T item = it.next();
...
}
Three Things Iterable Gives You
- ›for-each loop support — the Java compiler checks
instanceof Iterableat compile time - ›
forEach()default method — lambda-based iteration introduced in Java 8 - ›
spliterator()default method — enables parallel iteration via the Streams API
Iterable vs Iterator — The Critical Distinction
These two names cause constant confusion for beginners. They are different interfaces with different roles.
Iterable<T> Iterator<T>
----------- -----------
Lives in: java.lang Lives in: java.util
Purpose : signals "I can produce Purpose : the actual cursor that
an iterator" does the traversal
Method : iterator() Methods : hasNext(), next(), remove()
Used as : a type you implement Used as : what iterator() returns
on your class
Relationship:
Iterable produces Iterator:
Iterator<T> it = myIterable.iterator();
Analogy (without using metaphors):
Iterable is a book — it can be read many times.
Iterator is a bookmark — it tracks one reading's current position.
Each new iterator() call creates a new bookmark at page 1.
// File: IterableVsIteratorDemo.java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IterableVsIteratorDemo {
public static void main(String[] args) {
List<String> cities = new ArrayList<>();
cities.add("Mumbai");
cities.add("Delhi");
cities.add("Bengaluru");
cities.add("Chennai");
// List is Iterable — supports for-each
System.out.println("=== for-each (uses Iterable under the hood) ===");
for (String city : cities) {
System.out.println(" " + city);
}
// iterator() produces a fresh Iterator each time
System.out.println("\n=== Two independent Iterators from the same Iterable ===");
Iterator<String> first = cities.iterator(); // cursor 1 — starts at position 0
Iterator<String> second = cities.iterator(); // cursor 2 — also starts at position 0
System.out.println("first.next() = " + first.next()); // Mumbai
System.out.println("first.next() = " + first.next()); // Delhi
System.out.println("second.next() = " + second.next()); // Mumbai — independent!
// Iterator is stateful — it remembers where it is
System.out.println("\n=== Iterator with hasNext and next ===");
Iterator<String> it = cities.iterator();
while (it.hasNext()) {
System.out.println(" " + it.next());
}
System.out.println("it.hasNext() after exhaustion: " + it.hasNext()); // false
}
}Output:
=== for-each (uses Iterable under the hood) ===
Mumbai
Delhi
Bengaluru
Chennai
=== Two independent Iterators from the same Iterable ===
first.next() = Mumbai
first.next() = Delhi
second.next() = Mumbai — independent!
=== Iterator with hasNext and next ===
Mumbai
Delhi
Bengaluru
Chennai
it.hasNext() after exhaustion: false
How for-each Works Internally
The enhanced for-each loop is purely compiler sugar — the Java compiler transforms it into an explicit Iterator loop before generating bytecode. Understanding this transformation explains why ConcurrentModificationException fires, why you cannot use list.remove() inside for-each, and why iterating a collection twice from two threads can cause problems.
// File: ForEachCompilationDemo.java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class ForEachCompilationDemo {
public static void main(String[] args) {
List<String> frameworks = new ArrayList<>();
frameworks.add("Spring");
frameworks.add("Hibernate");
frameworks.add("Kafka");
// What you write:
System.out.println("=== for-each loop ===");
for (String framework : frameworks) {
System.out.println(" " + framework);
}
// What the compiler generates — byte-for-byte equivalent:
System.out.println("\n=== compiler expansion ===");
for (Iterator<String> it = frameworks.iterator(); it.hasNext(); ) {
String framework = it.next();
System.out.println(" " + framework);
}
// Why list.remove() inside for-each throws ConcurrentModificationException:
// The iterator checks modCount on every next() call.
// list.remove() increments modCount.
// The iterator detects the mismatch and throws.
System.out.println("\n=== safe removal — use iterator.remove() ===");
Iterator<String> removalIt = frameworks.iterator();
while (removalIt.hasNext()) {
String fw = removalIt.next();
if (fw.equals("Hibernate")) {
removalIt.remove(); // safe — iterator updates its own expectedModCount
}
}
System.out.println("After safe removal: " + frameworks);
// Java 8+ forEach default method — also comes from Iterable
System.out.println("\n=== Iterable.forEach (Java 8 default method) ===");
frameworks.forEach(fw -> System.out.println(" " + fw));
}
}Output:
=== for-each loop ===
Spring
Hibernate
Kafka
=== compiler expansion ===
Spring
Hibernate
Kafka
=== safe removal — use iterator.remove() ===
After safe removal: [Spring, Kafka]
=== Iterable.forEach (Java 8 default method) ===
Spring
Kafka
When to Implement Iterable
The Iterable interface on a class communicates one thing clearly: instances of this class represent a sequence of elements that callers can traverse. Implement it when:
- ›Your class wraps or manages a collection of objects that callers need to iterate
- ›You want your class to work naturally in for-each loops
- ›You want to pass instances of your class to any method that accepts
Iterable<T> - ›You want callers to use
forEach()and work with the Streams API viaspliterator()
Do not implement Iterable on a class just because it internally holds a collection. A TransactionProcessor that has a List<Transaction> internally is not a collection of transactions in the caller's mental model — expose a method like getTransactions() that returns the list instead. Reserve Iterable for classes that are themselves meaningfully a sequence.
Implementing a Custom Iterable
The most direct way to understand Iterable is to build a class that implements it from scratch. The example below models a simple paginated data reader that yields records one at a time, independent of how many pages the underlying source has.
// File: NumberRange.java
import java.util.Iterator;
import java.util.NoSuchElementException;
// A custom Iterable that yields integers from start to end (inclusive)
public class NumberRange implements Iterable<Integer> {
private final int start;
private final int end;
public NumberRange(int start, int end) {
if (start > end) throw new IllegalArgumentException(
"start must be <= end. Got: " + start + " > " + end);
this.start = start;
this.end = end;
}
// Each call to iterator() creates a fresh, independent cursor
@Override
public Iterator<Integer> iterator() {
return new RangeIterator();
}
// Private inner class holds the traversal state
private class RangeIterator implements Iterator<Integer> {
private int current = start;
@Override
public boolean hasNext() {
return current <= end;
}
@Override
public Integer next() {
if (!hasNext()) {
// Contract requires NoSuchElementException when exhausted
throw new NoSuchElementException("No more elements in range " + start + ".." + end);
}
return current++;
}
// remove() is optional — throw if not supported
@Override
public void remove() {
throw new UnsupportedOperationException("NumberRange does not support removal");
}
}
}// File: NumberRangeDemo.java
import java.util.ArrayList;
import java.util.List;
public class NumberRangeDemo {
public static void main(String[] args) {
NumberRange range = new NumberRange(1, 10);
// Works in for-each because NumberRange implements Iterable<Integer>
System.out.println("=== for-each on custom Iterable ===");
for (int n : range) {
System.out.print(n + " ");
}
System.out.println();
// Each iterator() call is independent — can iterate twice
System.out.println("\n=== Two independent iterations ===");
System.out.print("Pass 1: ");
for (int n : range) { System.out.print(n + " "); }
System.out.println();
System.out.print("Pass 2: ");
for (int n : range) { System.out.print(n + " "); }
System.out.println();
// Works with forEach default method (from Iterable, Java 8+)
System.out.println("\n=== Iterable.forEach with lambda ===");
new NumberRange(1, 5).forEach(n -> System.out.print(n * n + " ")); // squares
System.out.println();
// Works with any method accepting Iterable<T>
System.out.println("\n=== Passed to a method accepting Iterable ===");
printAll(new NumberRange(5, 9));
// Works to build a collection
System.out.println("\n=== Collect into a List ===");
List<Integer> collected = new ArrayList<>();
for (int n : new NumberRange(1, 5)) {
collected.add(n);
}
System.out.println("Collected: " + collected);
}
static void printAll(Iterable<Integer> items) {
System.out.print("printAll: ");
for (int item : items) {
System.out.print(item + " ");
}
System.out.println();
}
}Output:
=== for-each on custom Iterable ===
1 2 3 4 5 6 7 8 9 10
=== Two independent iterations ===
Pass 1: 1 2 3 4 5 6 7 8 9 10
Pass 2: 1 2 3 4 5 6 7 8 9 10
=== Iterable.forEach with lambda ===
1 4 9 16 25
=== Passed to a method accepting Iterable ===
printAll: 5 6 7 8 9
=== Collect into a List ===
Collected: [1, 2, 3, 4, 5]
Iterable with Java 8 forEach and Streams
Java 8 added two default methods to Iterable. They are inherited by every collection automatically, but they are also available on any custom class that implements Iterable.
// File: IterableJava8Demo.java
import java.util.ArrayList;
import java.util.List;
import java.util.Spliterator;
import java.util.Spliterators;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
public class IterableJava8Demo {
public static void main(String[] args) {
List<String> products = new ArrayList<>(
List.of("Laptop", "Mouse", "Keyboard", "Monitor", "Webcam")
);
// forEach — default method on Iterable
System.out.println("=== Iterable.forEach ===");
products.forEach(product -> System.out.println(" " + product));
// forEach with method reference — cleaner for simple print
System.out.println("\n=== forEach with method reference ===");
products.forEach(System.out::println);
// spliterator() — default method on Iterable, used by Stream
System.out.println("\n=== Stream from custom Iterable (via spliterator) ===");
NumberRange evenNumbers = new NumberRange(1, 20);
List<Integer> evens = StreamSupport
.stream(evenNumbers.spliterator(), false) // false = sequential
.filter(n -> n % 2 == 0)
.collect(Collectors.toList());
System.out.println("Even numbers 1..20: " + evens);
// Standard collections — stream() is direct, but spliterator still works
System.out.println("\n=== Filtering with forEach conditional ===");
products.forEach(p -> {
if (p.length() > 5) {
System.out.println(" Long name: " + p);
}
});
}
}Output:
=== Iterable.forEach ===
Laptop
Mouse
Keyboard
Monitor
Webcam
=== forEach with method reference ===
Laptop
Mouse
Keyboard
Monitor
Webcam
=== Stream from custom Iterable (via spliterator) ===
Even numbers 1..20: [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
=== Filtering with forEach conditional ===
Long name: Keyboard
Long name: Monitor
Basic Example
The smallest way to see Iterable at work is to write a tiny class that holds a few items and implements Iterable<String> so it can be used directly in a for-each loop.
import java.util.Iterator;
public class Main {
public static void main(String[] args) {
FruitBasket basket = new FruitBasket();
basket.add("Apple");
basket.add("Banana");
basket.add("Mango");
// Works because FruitBasket implements Iterable<String>
for (String fruit : basket) {
System.out.println(fruit);
}
}
}
class FruitBasket implements Iterable<String> {
private String[] fruits = new String[10];
private int count = 0;
public void add(String fruit) {
fruits[count++] = fruit;
}
@Override
public Iterator<String> iterator() {
return new Iterator<String>() {
private int index = 0;
@Override
public boolean hasNext() {
return index < count;
}
@Override
public String next() {
return fruits[index++];
}
};
}
}Output
Apple Banana Mango
How it works:
- ›
FruitBasketimplementsIterable<String>, so its only job is to provide a workingiterator()method. - ›
iterator()returns an anonymousIterator<String>that keeps its ownindexfield to remember where it is. - ›Because
FruitBasketisIterable, the compiler acceptsfor (String fruit : basket)and internally callsiterator(), thenhasNext()andnext()until the basket is exhausted. - ›Any class you write can plug into for-each the same way — implement
Iterable, return a freshIteratoreach time, and Java handles the rest.
Performance Considerations
Iterable itself introduces no overhead — it is an interface with no state. The performance of iteration depends entirely on the Iterator implementation the class returns.
| Collection | Iterator type | hasNext() | next() | Notes |
|---|---|---|---|---|
| ArrayList | Index-based | O(1) | O(1) | Direct array access — cache-friendly |
| LinkedList | Node-based | O(1) | O(1) | Follows node.next reference — cache-unfriendly |
| HashSet | Bucket scan | O(1) amort | O(1) amort | Scans empty buckets between elements |
| TreeSet | In-order tree | O(1) amort | O(1) amort | Follows left/right/parent pointers |
| HashMap entrySet | Bucket scan | O(1) amort | O(1) amort | Similar to HashSet traversal |
| Custom Iterable | Depends | Depends | Depends | Only as fast as your iterator() impl |
Fail-fast iterators: Most standard Collection implementations return fail-fast iterators. They track an internal modCount counter. If the collection is structurally modified while iteration is in progress — by calling list.add() or list.remove() outside the iterator — the iterator throws ConcurrentModificationException on the next next() call. Custom Iterable implementations need to decide whether to implement fail-fast behaviour.
Custom Iterable and Streams: Any class implementing Iterable<T> can be converted to a Stream<T> via StreamSupport.stream(iterable.spliterator(), false). This gives full access to filter, map, collect, and all other stream operations without needing to extend any collection class.
Best Practices
Implement Iterable<T> only when the class is logically a container of traversable elements. A class like OrderHistory, ProductCatalogue, or InventoryBatch represents a sequence of things — Iterable fits naturally. A class like OrderService or ReportGenerator that happens to hold a list internally should not implement Iterable. Expose the list through a getter and let callers iterate the list directly.
Always return a fresh, independent Iterator from every iterator() call. Two concurrent for-each loops on the same Iterable must not share state. Each iterator() call must return a new Iterator object with its own cursor at position zero. During code reviews, a common fresher mistake is returning a shared iterator held as a field — this makes the second for-each loop start mid-sequence.
Throw NoSuchElementException in next() when the iterator is exhausted. This is the contract specified in Iterator's documentation. Throwing anything else — RuntimeException, IndexOutOfBoundsException — violates the contract and breaks any caller that catches NoSuchElementException for safe termination. Always check hasNext() first inside next().
Use Iterable<T> as a parameter type for the widest possible acceptance. A method declared void process(Iterable<Product> items) accepts ArrayList<Product>, HashSet<Product>, TreeSet<Product>, and any custom class implementing Iterable<Product>. A method declared void process(List<Product> items) only accepts List implementations. Always use the widest contract that the method actually needs.
Common Mistakes
Mistake 1 — Modifying the Collection Inside a for-each Loop
List<String> items = new ArrayList<>(List.of("A", "B", "C", "D"));
// WRONG — for-each uses an iterator internally;
// list.remove() increments modCount and triggers ConcurrentModificationException
for (String item : items) {
if (item.equals("B")) {
items.remove(item); // throws ConcurrentModificationException at runtime
}
}
// CORRECT — use removeIf (Java 8+) or explicit iterator.remove()
items.removeIf("B"::equals);
// OR use explicit iterator
Iterator<String> it = items.iterator();
while (it.hasNext()) {
if (it.next().equals("B")) {
it.remove(); // updates expectedModCount — safe
}
}Mistake 2 — Returning a Shared Iterator from iterator()
// WRONG — all callers share one iterator; second for-each starts mid-sequence
public class BrokenIterable implements Iterable<String> {
private final List<String> data = List.of("A", "B", "C");
private final Iterator<String> sharedIt = data.iterator(); // shared state — BUG
@Override
public Iterator<String> iterator() {
return sharedIt; // first call: starts at A; second call: starts wherever first left off
}
}
// CORRECT — always create a fresh iterator
@Override
public Iterator<String> iterator() {
return data.iterator(); // fresh cursor at position 0 each time
}Mistake 3 — Forgetting to Check hasNext() Before Calling next()
Iterator<String> it = someList.iterator();
// WRONG — next() throws NoSuchElementException if the list is empty
String first = it.next(); // crashes on empty list
// CORRECT — always guard with hasNext()
if (it.hasNext()) {
String first = it.next(); // safe
}
// For the most-common case, for-each handles this automatically
for (String s : someList) { // no NoSuchElementException risk
process(s);
}Mistake 4 — Implementing Iterable When a Getter Is More Appropriate
// WRONG — OrderService is not a container; implementing Iterable is confusing
public class OrderService implements Iterable<Order> {
private final List<Order> orders = new ArrayList<>();
@Override
public Iterator<Order> iterator() { return orders.iterator(); }
// Now callers write: for (Order o : orderService) { ... }
// This reads like "for each order in the service" — odd phrasing
}
// CORRECT — expose the list; let callers iterate it naturally
public class OrderService {
private final List<Order> orders = new ArrayList<>();
public List<Order> getOrders() {
return Collections.unmodifiableList(orders); // safe read-only view
}
// Callers write: for (Order o : orderService.getOrders()) { ... }
// Much clearer about what is being iterated
}Interview Questions
Q1. What is the Iterable interface in Java and what is its purpose?
java.lang.Iterable<T> is an interface in java.lang that declares one abstract method: Iterator<T> iterator(). Any class that implements it can be used in a Java enhanced for-each loop. The Java compiler translates for (T item : myObject) into an explicit Iterator loop — calling iterator() to get a cursor, then repeatedly calling hasNext() and next(). All Collection implementations (ArrayList, HashSet, HashMap's views) implement Iterable, which is why every collection works with for-each. A class you write yourself can also support for-each simply by implementing Iterable.
Q2. What is the difference between Iterable and Iterator?
Iterable<T> is implemented by a class to signal that it can produce traversal cursors — its one method, iterator(), returns an Iterator. Iterator<T> is the cursor itself — it has hasNext() to check if more elements exist and next() to retrieve the current element and advance. Iterable is the container; Iterator is the one-time traversal state object. Each call to iterator() must return a new, independent Iterator starting at the beginning. Iterable is in java.lang; Iterator is in java.util.
Q3. Why does the for-each loop throw ConcurrentModificationException?
The for-each loop on a collection uses the collection's Iterator internally. Most collection iterators are fail-fast — they track the collection's modCount (an internal counter incremented on every structural change) at the time the iterator is created. On every call to next(), the iterator checks whether modCount still matches. If the collection was modified outside the iterator — by calling list.add() or list.remove() during the loop — modCount no longer matches and ConcurrentModificationException is thrown immediately. Fix it with iterator.remove() or list.removeIf() — both update the expected count safely.
Q4. Can you implement Iterable on a class that does not hold a collection?
Yes. Iterable requires only that iterator() returns a valid Iterator<T> — how that iterator computes values is entirely up to the implementation. A range object, a file reader, a database result wrapper, a generator that computes values on demand, or a Fibonacci sequence can all implement Iterable<T> without holding any backing collection. The iterator simply needs to implement hasNext() and next() correctly for whatever computation or source it wraps.
Q5. What does Java 8 add to the Iterable interface?
Java 8 added two default methods to Iterable. forEach(Consumer<? super T> action) iterates the elements and applies the given lambda or method reference to each one — a clean alternative to writing a for-each loop. spliterator() returns a Spliterator<T> that enables parallel iteration via the Streams API. These are default methods, so all existing implementations inherit them without any code change. They are available on every collection automatically and on any custom Iterable class.
Q6. How do you create a Stream from a custom Iterable?
Use StreamSupport.stream(iterable.spliterator(), false). The first argument is the Spliterator obtained from Iterable.spliterator() (the default method). The second argument is false for a sequential stream or true for a parallel stream. This gives full access to all stream operations — filter, map, collect, reduce — for any class that implements Iterable<T>, without extending any collection class.
FAQs
Does every Java collection implement Iterable?
Every class in the Collection hierarchy does — ArrayList, LinkedList, HashSet, TreeSet, ArrayDeque, and all others. Map does not directly implement Iterable, but its views do: map.keySet() returns a Set (which is Iterable), map.values() returns a Collection (which is Iterable), and map.entrySet() returns a Set<Map.Entry<K,V>> (also Iterable). Arrays do not implement Iterable but support for-each through special compiler handling.
Can I use a Java array in a for-each loop if it does not implement Iterable?
Yes — the Java compiler handles arrays as a special case. for (int n : intArray) compiles to a standard index-based loop, not an iterator-based one. Arrays are the only type that supports for-each without implementing Iterable. You cannot pass an array where Iterable<T> is required as a parameter type — for that, use Arrays.asList(array) to get a List that implements Iterable.
What happens if iterator() returns null?
The for-each loop calls iterator() and immediately calls hasNext() on the result. Returning null from iterator() causes a NullPointerException at the first hasNext() call inside the loop. This is considered a contract violation. If the iterable is empty, return an iterator whose hasNext() returns false immediately — never return null from iterator().
Is Iterable thread-safe?
The Iterable interface itself makes no thread-safety guarantees. Whether iteration is thread-safe depends entirely on the implementation. Standard ArrayList and HashMap iterators are not thread-safe. Concurrent modification from another thread while iterating throws ConcurrentModificationException. For thread-safe iteration, use CopyOnWriteArrayList (snapshot iterator, never throws CME) or iterate within a synchronized block on Collections.synchronizedList().
What is the difference between iterator() and listIterator()?
iterator() is defined by Iterable/Collection — it returns an Iterator<T> that traverses forward only with hasNext(), next(), and remove(). listIterator() is defined by the List interface — it returns a ListIterator<T> which extends Iterator with backward traversal (hasPrevious(), previous()), index queries (nextIndex(), previousIndex()), and in-place modification (set(), add()). ListIterator is only available on List implementations; Iterator is available on all Iterable types.
Why is Iterable in java.lang instead of java.util?
Placing Iterable in java.lang (which is auto-imported) means the for-each loop does not require developers to import anything extra for their own classes to support it. If Iterable were in java.util, every custom class implementing it would need an explicit import. The Java designers placed it in java.lang to make the for-each syntax as lightweight as possible — implement one method from one interface with no import required, and your class immediately works in for-each loops everywhere.
Summary
Iterable<T> is the single interface that connects any Java class to the for-each loop. It lives in java.lang, requires one method — iterator() — and is the root of the entire Collection hierarchy. Every ArrayList, HashSet, TreeMap's key set, and any other standard collection supports for-each because it implements Iterable.
The key internal fact: the compiler expands for (T item : obj) into an explicit Iterator loop — obj.iterator() is called once, then hasNext() and next() drive the loop. This is why structural modification during for-each triggers ConcurrentModificationException, and why iterator.remove() is the only safe removal during traversal.
For production code: use Iterable<T> as a parameter type for maximum flexibility. For custom classes: implement Iterable<T> only when the class is semantically a container of traversable elements. Always return a fresh Iterator from every iterator() call. Throw NoSuchElementException from next() when exhausted.
What to Read Next
Learn the interface that most collections are built on.
Practice Programs
Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.
Walks a list with an explicit Iterator and removes matching elements safely during iteration.
Walks through an ArrayList three ways: a for-each loop, an explicit Iterator, and an index-based loop.