Java Tutorial
🔍

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 Iterable at 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 via spliterator()

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:

  1. ›FruitBasket implements Iterable<String>, so its only job is to provide a working iterator() method.
  2. ›iterator() returns an anonymous Iterator<String> that keeps its own index field to remember where it is.
  3. ›Because FruitBasket is Iterable, the compiler accepts for (String fruit : basket) and internally calls iterator(), then hasNext() and next() until the basket is exhausted.
  4. ›Any class you write can plug into for-each the same way — implement Iterable, return a fresh Iterator each 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.

CollectionIterator typehasNext()next()Notes
ArrayListIndex-basedO(1)O(1)Direct array access — cache-friendly
LinkedListNode-basedO(1)O(1)Follows node.next reference — cache-unfriendly
HashSetBucket scanO(1) amortO(1) amortScans empty buckets between elements
TreeSetIn-order treeO(1) amortO(1) amortFollows left/right/parent pointers
HashMap entrySetBucket scanO(1) amortO(1) amortSimilar to HashSet traversal
Custom IterableDependsDependsDependsOnly 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

Practice Programs

Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.

Browse all Java programs →