Java Iterable Interface
Last modified: April 13, 2025
The java.lang.Iterable
interface is a fundamental interface in Java
that represents a collection of elements which can be iterated over. It's the
superinterface of all collection interfaces in the Java Collections Framework.
Implementing Iterable allows an object to be the target of the enhanced for-loop.
The Iterable interface provides methods to obtain an iterator over elements of a collection. It was introduced in Java 5 and enhanced in Java 8 with default methods. Understanding Iterable is crucial for working with Java collections and creating custom iterable data structures.
Iterable Interface Methods
The Iterable interface defines three methods that provide iteration capabilities.
The main method is iterator
, while forEach
and
spliterator
were added as default methods in Java 8.
public interface Iterable<T> { Iterator<T> iterator(); default void forEach(Consumer<? super T> action) {...} default Spliterator<T> spliterator() {...} }
The code above shows the methods provided by the Iterable interface. The
iterator
method must be implemented, while the other two have
default implementations but can be overridden for custom behavior.
Basic Iterable Implementation
This example demonstrates how to implement the Iterable interface in a custom class. We create a simple collection-like class that holds elements and provides iteration capability.
package com.zetcode; import java.util.Iterator; import java.util.NoSuchElementException; class SimpleCollection<T> implements Iterable<T> { private T[] elements; private int size; @SuppressWarnings("unchecked") public SimpleCollection(int capacity) { elements = (T[]) new Object[capacity]; size = 0; } public void add(T element) { if (size < elements.length) { elements[size++] = element; } } @Override public Iterator<T> iterator() { return new Iterator<T>() { private int currentIndex = 0; @Override public boolean hasNext() { return currentIndex < size; } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } return elements[currentIndex++]; } }; } } void main() { SimpleCollection<String> collection = new SimpleCollection<>(3); collection.add("First"); collection.add("Second"); collection.add("Third"); for (String item : collection) { System.out.println(item); } }
In this example, we create a SimpleCollection
class that implements
Iterable. The class maintains an array of elements and provides an iterator
implementation as an anonymous inner class. The enhanced for-loop works with our
custom collection because it implements Iterable.
Using forEach Method
The forEach
method, added in Java 8, performs the given action for
each element of the Iterable until all elements have been processed. This method
simplifies iteration with lambda expressions.
package com.zetcode; import java.util.Arrays; import java.util.List; void main() { List<String> languages = Arrays.asList("Java", "Python", "C++", "JavaScript"); // Traditional for-loop System.out.println("Traditional iteration:"); for (String lang : languages) { System.out.println(lang); } // Using forEach with lambda System.out.println("\nUsing forEach:"); languages.forEach(lang -> System.out.println(lang)); // Using method reference System.out.println("\nUsing method reference:"); languages.forEach(System.out::println); }
This example shows different ways to iterate over a List (which implements
Iterable). The forEach
method provides a concise way to process
each element using a lambda expression or method reference, making the code more
readable and expressive.
Custom Iterator Implementation
This example demonstrates how to create a more sophisticated custom iterator for a specialized data structure. We'll implement a range iterator that generates numbers within a specified range.
package com.zetcode; import java.util.Iterator; class NumberRange implements Iterable<Integer> { private final int start; private final int end; private final int step; public NumberRange(int start, int end, int step) { this.start = start; this.end = end; this.step = step; } @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { private int current = start; @Override public boolean hasNext() { return step > 0 ? current <= end : current >= end; } @Override public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } int value = current; current += step; return value; } }; } } void main() { System.out.println("Positive step:"); for (int num : new NumberRange(1, 10, 2)) { System.out.println(num); } System.out.println("\nNegative step:"); for (int num : new NumberRange(10, 1, -2)) { System.out.println(num); } }
This example creates a NumberRange
class that generates numbers
from start to end with a given step. The iterator handles both positive and
negative steps correctly. The implementation shows how to create a stateful
iterator that maintains its position during iteration.
Spliterator Example
The spliterator
method returns a Spliterator over the elements
described by this Iterable. Spliterators are used for parallel processing and
more sophisticated traversal operations.
package com.zetcode; import java.util.Arrays; import java.util.Spliterator; import java.util.List; void main() { List<String> fruits = Arrays.asList("Apple", "Banana", "Cherry", "Date", "Elderberry"); // Get spliterator Spliterator<String> spliterator = fruits.spliterator(); // Try to process elements one by one System.out.println("Individual elements:"); while (spliterator.tryAdvance(System.out::println)) {} // Split and process in parallel System.out.println("\nSplit processing:"); Spliterator<String> split1 = spliterator.trySplit(); if (split1 != null) { System.out.println("Split1:"); split1.forEachRemaining(System.out::println); } System.out.println("Original:"); spliterator.forEachRemaining(System.out::println); }
This example demonstrates basic Spliterator usage. We first process elements one
by one with tryAdvance
, then split the spliterator to show how it
can divide work for parallel processing. Spliterators are particularly useful
for parallel streams and bulk data operations.
Iterable with Primitive Types
While Iterable works with objects, we can create specialized implementations for primitive types using wrapper classes. This example shows an iterable for primitive int values.
package com.zetcode; import java.util.Iterator; import java.util.NoSuchElementException; class IntRange implements Iterable<Integer> { private final int[] values; public IntRange(int... values) { this.values = values; } @Override public Iterator<Integer> iterator() { return new Iterator<Integer>() { private int index = 0; @Override public boolean hasNext() { return index < values.length; } @Override public Integer next() { if (!hasNext()) { throw new NoSuchElementException(); } return values[index++]; } }; } } void main() { IntRange range = new IntRange(10, 20, 30, 40, 50); // Using enhanced for-loop for (int num : range) { System.out.println(num); } // Using forEach System.out.println("\nUsing forEach:"); range.forEach(num -> System.out.println(num * 2)); }
This example creates an IntRange
class that holds primitive int
values but provides iteration through Integer objects. The class demonstrates
how to work with primitive values while still implementing the Iterable
interface for use with Java's collection framework.
Nested Iterable Implementation
This example shows how to implement Iterable for a more complex data structure with nested elements. We'll create a matrix class that allows iteration through all elements row by row.
package com.zetcode; import java.util.Iterator; import java.util.NoSuchElementException; class Matrix<T> implements Iterable<T> { private final T[][] data; @SuppressWarnings("unchecked") public Matrix(int rows, int cols) { data = (T[][]) new Object[rows][cols]; } public void set(int row, int col, T value) { data[row][col] = value; } @Override public Iterator<T> iterator() { return new Iterator<T>() { private int row = 0; private int col = 0; @Override public boolean hasNext() { return row < data.length && col < data[row].length; } @Override public T next() { if (!hasNext()) { throw new NoSuchElementException(); } T value = data[row][col]; // Move to next element col++; if (col >= data[row].length) { col = 0; row++; } return value; } }; } } void main() { Matrix<String> matrix = new Matrix<>(2, 3); matrix.set(0, 0, "A1"); matrix.set(0, 1, "A2"); matrix.set(0, 2, "A3"); matrix.set(1, 0, "B1"); matrix.set(1, 1, "B2"); matrix.set(1, 2, "B3"); System.out.println("Matrix elements:"); for (String element : matrix) { System.out.println(element); } }
This example implements a Matrix
class that stores elements in a
2D array but provides a flat iteration through all elements. The iterator
maintains its position across rows and columns, demonstrating how to handle
more complex iteration scenarios while still implementing the Iterable interface.
Source
Java Iterable Interface Documentation
In this article, we've covered the Java Iterable interface with practical examples. Understanding Iterable is essential for working with Java collections and creating custom iterable data structures that work seamlessly with Java's enhanced for-loop and stream API.
Author
List all Java tutorials.