Can we use for-each loop for iterating the objects of Iterator type? [duplicate]
If we do the following we get error:
class FGH{
public static Iterator reverse(List list) {
Collections.reverse(list);
return list.iterator();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
/*for(Iterator it:reverse(list))
Iterator it=reverse(list);*/
for (Object obj: reverse(list))
System.out.print(obj + ", ");}}
but if we modify the code like this we don't get error,so is it mean that we can't iterate the objects of Iterator type? :
class FGH{
public static Iterator reverse(List list) {
Collections.reverse(list);
return list.iterator();
}
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
Iterator it=reverse(list);
while(it.hasNext()){
Object obj=it.next();
System.out.println(obj);
}
}}
A lot of answers talk about how iterators are not iterables. That's true, but such an answer doesn't touch on why.
The reason for-each loops require an iterable is to allow the same object to be traversed multiple times (so that you can use multiple for-each loops over the same object without surprising behaviour), whereas an iterator only allows one traversal. If for-each allowed iterators to be used, the behaviour would be surprising to programmers who didn't realise that their iterators would be exhausted after the loop is run.
If you're using an API that only gives you iterators, and you want to use iterables, you have two ways to solve this:
- Make an anonymous iterable class, whose
iterator()
method calls the API function that returns the iterator. That way, each time you use a for-each loop on the iterable object, that API function is called again, returning a new (and unexhausted) iterator. - Make a one-pass iterable wrapper class that takes an iterator and allows one call to
iterator()
. On subsequent calls, throw anAssertionError
orIllegalStateException
as appropriate.
The for
loop in your first example expects that reverse(list)
is a collection of Iterator
, which of course it isn't. That's why that example won't work.
In general, you can only use foreach
on classes that implement Iterable
. Iterator
is not one of these classes.
See http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Iterable.html
You would have to wrap your Iterator
in an Iterable
first. Here's a Java 8 solution:
for (Object obj : (Iterable<Object>) () -> reverse(list))
System.out.print(obj + ", ");
Or, just use Iterator.forEachRemaining()
:
reverse(list).forEachRemaining(obj -> System.out.print(obj + ", "));
You'd use the enhanced for loop in this way:
for (Object o : list)
As pointed out above, you need to reference an Iterable (or array) in a for-each block. For example, when you do the following:
Collection<String> coll = ...;
for (String str : coll) {
...
}
The following is really happening:
Collection<String> coll = ...;
for (Iterator<String> iter = coll.iterator(); iter.hasNext(); ) {
String str = iter.next();
}
In order to be able to do this, the argument has to implement Iterable
(e.g. have a public iterator()
method that returns an Iterator
(or be an array). So your code should look like the following:
class FGH {
public static void main(String[] args) {
List list = new ArrayList();
list.add("1"); list.add("2"); list.add("3");
while (Object obj : Collections.reverse(list)){
System.out.println(obj);
}
}
}
The "for each" syntax is designed for collections (Iterable
to be precise), not for Iterators. I'm trying to find out the arguments of why Java was desinged this way in this question.
The simplest solution for you would be to return the reversed list (which is iterable) instead of a list iterator. Then you can use the shorthand for loop syntax.
A hackish way is outlined in the question mentioned earlier, using an Adapter class that wraps the Iterator
as Iterable
, and returns the Iterator
on the first invocation of iterator()
. Look at the adapter code, it's fairly simple. It serves the purpose, but since the iterator can only be used once, it is somewhat an invalid Iterable
(it will not be able produce a second iterator).
The key behavior difference shows up when you use it twice:
for(Object a : foo) { }
for(Object a : foo) { }
will process all elements in foo twice if it is a proper Iterable
, but only once if it is using the adapter I sketched - the second loop will do nothing.
精彩评论