开发者

Can we use for-each loop for iterating the objects of Iterator type? [duplicate]

开发者 https://www.devze.com 2022-12-18 06:53 出处:网络
This question already has answers here: Idiomatic way to use for-each loop given an iterator? (9 answers)
This question already has answers here: Idiomatic way to use for-each loop given an iterator? (9 answers) Closed 3 years ago开发者_运维知识库.

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:

  1. 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.
  2. Make a one-pass iterable wrapper class that takes an iterator and allows one call to iterator(). On subsequent calls, throw an AssertionError or IllegalStateException 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.

0

精彩评论

暂无评论...
验证码 换一张
取 消