Skip to main content

Make java.util.Iterator usable in a for statement

11 replies [Last post]
subanark
Offline
Joined: 2004-11-26

Under the current symantics only classes which implement java.lang.Iterable may be used in a "foreach" statement. Since all this interface requires is to return an Iterator, it would be nice if there was a special case for Iterator.
I find that I end up with an iterator and not an Iterable often since I use legacy code that does not implement this interface, or I'm using a class which can be iterated in multiple ways.

<br />
for(Object o:myObj.getIterator())<br />
{ ... }<br />

The only reason I can think that having an iterator would not be allowed in a for statement is that Iterator is not in java.lang or one of its subpackages.

Message was edited by: subanark

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
vpatryshev
Offline
Joined: 2004-06-30

I do not think it would be a good idea to mix the notions of Iterator and Iterable. An Iterable is something that can return an iterator, and maybe not once; an Iterator, on the other hand, never goes back. On some occasions, like in the case of list, you can be kind of agnostic here, but lists are not all we have in life.

cbare
Offline
Joined: 2004-02-02

OK, I'll buy the explaination that something like this:

List list = getWidgets();

is probably better practice than:

Iterator iterator = getWidgetsIterator();

Also, code like this would be broken:

// match up all combinations of drinks and food
for (Drink drink : DrinkCollection) {
for (Food food : FoodIterator) {
System.out.println("You could have "
+ food + " and " + drink + "!");
}
}

The inner loop dies on the second pass of the outer loop. But, I still expected an Iterator to be iterable. All this protecting me from myself makes me want to go code up some non-bounds-checked array accesses in C just to keep my edge.

seanreilly
Offline
Joined: 2004-08-30

Use this:

public class IteratorContainer implements Iterable
{
private final Iterator it;
public IteratorContainer(final Iterator i)
{
this.it = i;
}

public java.util.Iterator iterator()
{
return this.it;
}
}

Then use it like this:
Iterator iterator = getWidgetIterator();
for (Widget w: new IteratorContainer(iterator))
{
//do something with the widget
}

vpatryshev
Offline
Joined: 2004-06-30

It is not a big deal to implement, I think. The background iterable ought to look like this:

[code]

Iterable iterateOnce(final Iterator iterator) {
public Iterator iterator() { return iterator; }
}

[/code]

And, say, introduce a coersion that makes an Iterator an Iterable in the right context.

As to the thin difference between an Iterable and Iterator that "Iterable" can return an Iterator many times, this is not necessary true. It is easy to imagine a singleton Iterator returned by and Iterable - so to say, "Iterable only once". You open an input stream, you do not iterate through it over and over again, right? But if it is a file, you can iterate through it as many times as you like.

markf
Offline
Joined: 2005-01-20

The problem isn't with the enhanced for loop or with iterators -- the problem is the legacy code. Better to spend effort improving and refactoring that legacy code than introduce troublesome, nonintuitive behaviour for iterators and enhanced for loops.

jwenting
Offline
Joined: 2003-12-02

An Iterator is a specific construct that handles its own iteration.
What you're trying to do is iterate externally over something that iterates internally, this leads to undefined behaviour.

Learn to use the correct means to iterate instead of trying to change the language to work around your misconceptions.

pauldv
Offline
Joined: 2005-02-03

> The only reason I can think that having an iterator
> would not be allowed in a for statement is that
> Iterator is not in java.lang or one of its
> subpackages.

The problem is more subtle. The contract of an Iterable is that one can create an iterator over the Iterable [b]repeatedly[/b]. It is not possible to reiterate an iterator.

In short, doing this would create unintuitive behaviour from the for loop. Such as the element to be iterated over, being invalid after use.

vpatryshev
Offline
Joined: 2004-06-30

Well, I would not agree. If possible, yes, it is good to be able to reproduce the iterator; but Iterable is still good even if it can return the iterator just once. E.g. bytes(Reader reader) - you cannot require the reader to be resettable, so what, that's life.

nick1304
Offline
Joined: 2005-01-05

A simple "hack" would be to create a IteratorLooper implments Iterable. It could accept an Iterator in its constructor and then just return that iterator in the iterator() call...

vpatryshev
Offline
Joined: 2004-06-30

Thanks for the good idea. I hope you do not object if I use it in my Iterators class on myjavatools.com

tackline
Offline
Joined: 2003-06-19

Some EA versions of the compiler permitted iterators in the enhanced for, IIRC.

I guess the problem is that the iterator is available to other statements, causing non-obvious behaviour. I'm quite happy that the facility has disappeared. In any case, it is better for APIs to return Collection types, as they are more flexible. There are not many cases where you do not want to hold onto all the objects at once, or ca not find the length (at all) if requested.

You can always load the elements of an Iterator (or Enumeration) into a Collection first. Off hand and without a compiler to check:

public static Collection collect(Iterator iter) {
` ` Collection collection = new ArrayList();
` ` while (iter.hasNext()) {
` ` ` ` collection.add(iter.next());
` ` }
` ` return collection;
}

` ` for (String name : collect(request.getParameterNames())) {
` ` ` ` ...
` ` }