Why does the Iterator
interface not extend extend Iterable
?
The iterator()
method could simply return ' this
'.
Is it on purpose or just an oversight of Java's designers?
It would be convenient to be able to use a for-each loop with iterators like this:
for(Object o : someContainer.listSomObjects()) {
....
}
where listSomeObject
returns an iterator.
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.
ReplyDeleteAn iterator is stateful. The idea is that if you call Iterable.iterator() twice you'll get independent iterators. That clearly wouldn't be the case in your scenario.
ReplyDeleteFor example, I can usually write:
public void iterateOver(Iterable<String> strings)
{
for (String x : strings)
{
System.out.println(x);
}
for (String x : strings)
{
System.out.println(x);
}
}
That should print the collection twice - but with your scheme the second loop would always terminate instantly.
For my $0.02, I completely agree that Iterator should not implement Iterable, but I think the enhanced for loop should accept either. I think the whole "make iterators iterable" argument comes up as a work around to a defect in the language.
ReplyDeleteThe whole reason for the introduction of the enhanced for loop was that it "eliminates the drudgery and error-proneness of iterators and index variables when iterating over collections and arrays" [1].
Collection<Item> items...
for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
Item item = iter.next();
...
}
for (Item item : items) {
...
}
Why then does this same argument not hold for iterators?
Iterator<Iter> iter...
..
while (iter.hasNext()) {
Item item = iter.next();
...
}
for (Item item : iter) {
...
}
In both cases, the calls to hasNext() and next() have been removed, and there is no reference to the iterator in the inner loop. Yes, I understand that Iterables can be re-used to create multiple iterators, but that all happens outside of the for loop: inside the loop there is only ever a forward progression one item at a time over the items returned by the iterator.
Also, allowing this would also make it easy to use the for loop for Enumerations, which, as has been pointed out elsewhere, are analogous to Iterators not Iterables.
So don't make Iterator implement Iterable, but update the for loop to accept either.
Cheers,
An Iterable is a thing from which you obtain an Iterator.
ReplyDeleteAs others have said, an Iterable can be called multiple times, returning a fresh Iterator on each call; an Iterator is used just once. So they are related, but serve different purposes. Frustratingly, however, the "compact for" method works only with an iterable.
ReplyDeleteWhat I will describe below is one way to have the best of both worlds - returning an Iterable (for nicer syntax) even when the underlying sequence of data is one-off.
The trick is to return an anonymous implementation of the Iterable that actually triggers the work. So instead of doing the work that generates a one-off sequence and then returning an Iterator over that, you return an Iterable which, each time it is accessed, redoes the work. That might seem wasteful, but often you will only call the Iterable once anyway, and even if you do call it multiple times, it still has reasonable semantics (unlike a simple wrapper that makes an Iterator "look like" an Iterable, this won't fail if used twice).
For example, say I have a DAO that provides a series of objects from a database, and I want to provide access to that via an iterator (eg. to avoid creating all objects in memory if they are not needed). Now I could just return an iterator, but that makes using the returned value in a loop ugly. So instead I wrap everything in an anon Iterable:
class MetricDao {
...
/**
* @return All known metrics.
*/
public final Iterable<Metric> loadAll() {
return new Iterable<Metric>() {
@Override
public Iterator<Metric> iterator() {
return sessionFactory.getCurrentSession()
.createQuery("from Metric as metric")
.iterate();
}
};
}
}
this can then be used in code like this:
class DaoUser {
private MetricDao dao;
for (Metric existing : dao.loadAll()) {
// do stuff here...
}
}
which lets me use the compact for loop while still keeping incremental memory use.
This approach is "lazy" - the work is not done when the Iterable is requested, but only later when the contents are iterated over - and you need to be aware of the consequences of that. In the example with a DAO that means iterating over the results within the database transaction.
So there are various caveats, but this can still be a useful idiom in many cases.
As pointed out by others, Iterator and Iterable are two different things.
ReplyDeleteAlso, Iterator implementations predate enhanced for loops.
It is also trivial to overcome this limitation with a simple adapter method that looks like this when used with static method imports:
for (String line : in(lines)) {
System.out.println(line);
}
Sample implementation:
/**
* Adapts an {@link Iterator} to an {@link Iterable} for use in enhanced for
* loops. If {@link Iterable#iterator()} is invoked more than once, an
* {@link IllegalStateException} is thrown.
*/
public static <T> Iterable<T> in(final Iterator<T> iterator) {
assert iterator != null;
class SingleUseIterable implements Iterable<T> {
private boolean used = false;
@Override
public Iterator<T> iterator() {
if (used) {
throw new IllegalStateException("SingleUseIterable already invoked");
}
used = true;
return iterator;
}
}
return new SingleUseIterable();
}
For the sake of simplicity, Iterator and Iterable are two distinct concepts, Iterable is simply a shorthand for "I can return an Iterator". I think that your code should be:
ReplyDeletefor(Object o : someContainer) {
}
with someContainer instanceof SomeContainer extends Iterable<Object>
I also see many doing this:
ReplyDeletepublic Iterator iterator() {
return this;
}
}
But that does not make it right!
This method would not be what you want!
The method iterator() is supposed to return a new iterator starting from scratch.
So one need to do something like this:
public class IterableIterator implements Iterator, Iterable {
//Constructor
IterableIterator(IterableIterator iter)
{
this.initdata = iter.initdata;
}
// methods of Iterable
public Iterator iterator() {
return new MyClass(this.somedata);
}
// methods of Iterator
public boolean hasNext() {
// ...
}
public Object next() {
// ...
}
public void remove() {
// ...
}
}
The question is: would there be any way to make an abstract class performing this?
So that to get an IterableIterator one only need to implement the two classes next() and hasNext()
As an aside: Scala has a toIterable() method in Iterator. See scala implicit or explicit conversion from iterator to iterable
ReplyDeleteVisage allready said that these are both different concepts introduced in different versions of Java. Iterator was introduced first, that's only one of the reasons it doesn't extend Iterable.
ReplyDeleteBut nothing can keep you from implementing both interfaces in one class. In fact this is the solution I see programmers use very often.
public class MyClass implements Iterator, Iterable {
// methods of Iterable
public Iterator iterator() {
return this;
}
// methods of Iterator
public boolean hasNext() {
// ...
}
public Object next() {
// ...
}
public void remove() {
// ...
}
}