Why is Java's Iterator not an Iterable?
Because an iterator generally points to a single instance in a collection. Iterable implies that one may obtain an iterator from an object to traverse over its elements - and there's no need to iterate over a single instance, which is what an iterator represents.
Why does Java not allow foreach on iterators (only on iterables)?
So I have a somewhat reasonable explanation now:
Short version: Because the syntax also applies to arrays, which don't have iterators.
If the syntax were designed around Iterator
as I proposed, it would be inconsistent with arrays. Let me give three variants:
A) as chosen by the Java developers:
Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list) { }
Iterator<Object> iter;
while(iter.hasNext()) { Object o = iter.next(); }
The behaves the same way and is highly consistent across arrays and collections.
Iterators however have to use the classic iteration style (which at least is not likely to cause errors).
B) allow arrays and Iterators
:
Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list.iterator()) { }
Iterator<Object> iter;
for(Object o : iter) { }
Now arrays and collections are inconsistent; but arrays and ArrayList are very closely related and should behave the same way. Now if at any point, the language is extended to make e.g. arrays implement Iterable
, it becomes inconsistent.
C) allow all three:
Object[] array;
for(Object o : array) { }
Iterable<Object> list;
for(Object o : list) { }
Iterator<Object> iter;
for(Object o : iter) { }
Now if we end up in unclear situations when either someone implements both Iterable
and Iterator
(is the for loop supposed to get a new iterator or iterate over the current - happens easily in tree-like structures!?!). A simple tie-braker ala "Iterable beats Iterator" unfortunately won't do: it suddenly introduces runtime vs. compile time difference and generics issues.
Now suddenly, we need to pay attention to whether we want to iterate over collections/iterables or arrays, at which point we have gained very little benefits at the cost of a big confusion.
The way "for each" is in Java (A) is very consistent, it causes very little programming errors, and it allows for the possible future change of turning arrays into regular objects.
There is a variant D) that would probably also work okay:
for-each for Iterators only. Preferrably by adding a .iterator()
method to primitive arrays:
Object[] array;
for(Object o : array.iterator()) { }
Iterable<Object> list;
for(Object o : list.iterator()) { }
Iterator<Object> iter;
for(Object o : iter) { }
But this requires changes to the runtime environment, not just the compiler, and breaks backwards compatibility. Plus, the mentioned confusion is still present that
Iterator<Object> iter;
for(Object o : iter) { }
for(Object o : iter) { }
Only iterates over the data once.
What is the difference between iterator and iterable and how to use them?
An Iterable
is a simple representation of a series of elements that can be iterated over. It does not have any iteration state such as a "current element". Instead, it has one method that produces an Iterator
.
An Iterator
is the object with iteration state. It lets you check if it has more elements using hasNext()
and move to the next element (if any) using next()
.
Typically, an Iterable
should be able to produce any number of valid Iterator
s.
Java: why can't iterate over an iterator?
but I still don't understand why this [...] was not made possible.
I can see several reasons:
Iterator
s are not reusable, so a for/each would consume the iterator - not incorrect behavior, perhaps, but unintuitive to those who don't know how the for/each is desugared.Iterator
s don't appear "naked" in code all that often so it would be complicating the JLS with little gain (the for/each construct is bad enough as it is, working on bothIterable
s and arrays).- There's an easy workaround. It may seem a little wasteful to allocate a new object just for this, but allocation is cheap as it is and escape analysis would rid you even of that small cost in most cases. (Why they didn't include this workaround in an
Iterables
utility class, analogous toCollections
andArrays
, is beyond me, though.) - (Probably not true - see the comments.)
I seem to recall that the JLS can only reference things injava.lang
[citation needed], so they'd have to create anIterator
interface injava.lang
whichjava.util.Iterator
extends without adding anything to. Now we have two functionally equivalent iterator interfaces. 50% of the new code using naked iterators will choose thejava.lang
version, the rest use the one injava.util
. Chaos ensues, compatibility problems abound, etc.
I think points 1-3 are very much in line with how the Java language design philosophy seems to go: Don't surprise newcomers, don't complicate the spec if it doesn't have a clear gain that overshadows the costs, and don't do with a language feature what can be done with a library.
The same arguments would explain why java.util.Enumeration
isn't Iterable
, too.
Why aren't Enumerations Iterable?
Enumeration hasn't been modified to support Iterable because it's an interface not a concrete class (like Vector, which was modifed to support the Collections interface).
If Enumeration was changed to support Iterable it would break a bunch of people's code.
Why does StreamT not implement IterableT?
People have already asked the same on the mailing list ☺. The main reason is Iterable also has a re-iterable semantic, while Stream is not.
I think the main reason is that
Iterable
implies reusability, whereasStream
is something that can only be used once — more like anIterator
.If
Stream
extendedIterable
then existing code might be surprised when it receives anIterable
that throws anException
the
second time they dofor (element : iterable)
.
Iterable and Iterator in java
You are right stating what these interfaces are used for. Indeed Iterable
declares that the objects of a class implementing it may be iterated over, by providjng an Iterator
specific to these objects. Having them is necessary because how exactly the object should be iterated depends on its internal implementation, and an Iterator
is therefore specific to a given "collection" class.
Having said that, it is worth noting that although these interfaces are formally a part of Java Collections framework, they can be applied to other cases. Given an imaginary API to read CSV files for example, one can declare a CsvFile
class to implement an Iterable<List<String>>
and iterate over lines in a file with a dedicated Iterator<List<String>>
which will read lines from the file one-by-one and split them into a List
of String
s to return it from next()
.
Another important purpose of these interfaces is a language feature known as "for each" loop - only objects of a class implementing Iterable
can be iterated with it. So, given an example from above about CsvFile
API, it will also enable something like:
CsvFile csvFile = new CsvFile(pathToCsvFile);
for (List<String> record : csvFile) {
doSomethingWithIt(record);
}
As "for each" loop is purely a language feature, compiler will expand it to use an Iterator
as usual.
P.S. Just because it hurts my eyes, I'd like to add that in the example above I would also suggest implementing an AutoCloseable
for the CsvFile
and using it with try-with-resources.
Why is it not possible for looping a class that directly implements Iterator?
Even though this kind of design is nothing but ridiculous, and just to entertain your question, you can write this:
public class Container implements Iterable<String>, Iterator<String> {
@Override public Iterator<String> iterator() { return this; }
... everything else you envision for your class ...
}
Then you'll be able to use
for (String s : container)
You may write this as a learning experience, but I strongly advise you never to try it on a real project. The main reason against it is the expectation on the iterator
method to always return a fresh, independent iterator, while this implementation achieves the opposite. To give a specific example, imagine someone needing to iterate over the Cartesian product of your container with itself:
for (String s1 : container)
for (String s2 : container)
processPair(s1, s2);
Every Java programmer will tell you that, beyond any doubt, processPair
will be called with each possible pair from the container. The behavior they would instead see from your class would make their heads spin. If a bug in production was ever traced to this implementation, your teammates would... let's just say they wouldn't appreciate it.
Related Topics
Parsing JSON in Java Without Knowing JSON Format
Retrieve Version from Maven Pom.Xml in Code
Using Gzip Compression with Spring Boot/Mvc/Javaconfig with Restful
Why Are Data Transfer Objects (Dtos) an Anti-Pattern
How to Remove Single Character from a String
Naming Threads and Thread-Pools of Executorservice
How to Convert Ascii Code (0-255) to Its Corresponding Character
Good Reasons to Prohibit Inheritance in Java
Increasing the Jvm Maximum Heap Size for Memory Intensive Applications
Declaring an Unsigned Int in Java
Open a Link in Browser with Java Button
Why Do We Need Immutable Class
Return Generated PDF Using Spring MVC
How to Generate the JPA Entity Metamodel
Java Array Hashcode Implementation
Any Way to Share Session State Between Different Applications in Tomcat