Idiomatic way to use for-each loop given an iterator?

后端 未结 9 1588
星月不相逢
星月不相逢 2020-12-03 09:52

When the enhanced for loop (foreach loop) was added to Java, it was made to work with a target of either an array or Iterable.

for ( T item : /*         


        
相关标签:
9条回答
  • 2020-12-03 10:17

    Why doesn't the enhanced for loop just accept an iterator?

    I want to gather a few of the potential reasons from the various answers as to why the for-each loop doesn't simply accept an iterator.

    1. Convenience: The for-each loop was created partly for convenience for the common operation of performing an action given each element of a collection. It has no obligation or intention of replacing the explicit use of iterators (obviously if you want to remove elements, you need an explicit reference to the iterator).
    2. Readability: The for-each loop for ( Row r : table ) is meant to be extremely readable as "for each row "r" in table...". Seeing for ( Row r : table.backwardsIterator() ) breaks that readability.
    3. Transparency: If an object is both an Iterable and an Iterator, what will be the behaviour? Though it's easy to make a consistent rule (e.g. Iterable before Iterator) the behaviour will be less transparent to developers. Furthermore, this will have to be checked at compile time.
    4. Encapsulation/Scope: This is (in my opinion) the most important reason. The for-each loop is designed to encapsulate the Iterator and limits its scope to the loop. This makes the loop "read-only" in two ways: it doesn't expose the iterator, meaning there is nothing (easily) tangible that has its state altered by the loop, nor can you alter the state of the operand in the loop (as you can by interfacing directly with an Iterator via remove()). Passing the Iterator yourself necessarily means the Iterator is exposed, making you lose both of those "read-only" attributes of the loop.
    0 讨论(0)
  • 2020-12-03 10:17

    The idiomatic way in Java 8 (being a verbose language) is this:

    for (T t : (Iterable<T>) () -> myDeque.descendingIterator()) {
      // use item
    }
    

    I.e. wrap the Iterator in an Iterable lambda. This is pretty much what you did yourself using an anonymous class, but it's a bit nicer with the lambda.

    Of course, you could always just resort to using Iterator.forEachRemaining():

    myDeque.descendingIterator().forEachRemaining(t -> {
      // use item
    });
    
    0 讨论(0)
  • 2020-12-03 10:18

    Guava does of course have a solution for the reverse iterable scenario, but unfortunately you need two Steps. Iterables.reverse() takes a List as parameter, not an Iterable.

    final Iterable<String> it = Arrays.asList("a", "b", "c");
    for(final String item : Iterables.reverse(Lists.newArrayList(it))){
        System.out.println(item);
    }
    

    Output:

    c
    b
    a

    0 讨论(0)
  • 2020-12-03 10:22

    Rather than create a descendingIterator, it would be better to write a descendingIterable() method to return a descending iterable based on a deque- which basically takes the place of your anonymous class. That seems pretty reasonable to me. As per Colin's suggestion, the iterable implementation returned by this method would call descendingIterator on the original deque each time its own iterator() method was called.

    If you've only got an iterator and want to keep it that way, you'd have to write an implementation of Iterable<T> which wrapped the iterator and returned it exactly once, throwing an exception if iterator() is called more than once. That would work, but it would clearly be pretty ugly.

    0 讨论(0)
  • 2020-12-03 10:24

    What I'd probably do is just make a utility class called Deques which could support this, along with other utilities if desired.

    public class Deques {
      private Deques() {}
    
      public static <T> Iterable<T> asDescendingIterable(final Deque<T> deque) {
        return new Iterable<T>() {
          public Iterator<T> iterator() {
            return deque.descendingIterator();
          }
        }
      }
    }
    

    This is another case where it's really too bad we don't have lambdas and method references yet. In Java 8, you'll be able to write something like this given that the method reference descendingIterator() matches the signature of Iterable:

    Deque<String> deque = ...
    for (String s : deque::descendingIterator) { ... }
    
    0 讨论(0)
  • 2020-12-03 10:32

    The Apache Commons Collections API has a class called IteratorIterable to do exactly this:

    Iterator<X> iter;
    for (X item : new IteratorIterable(iter)) {
        ...
    }
    
    0 讨论(0)
提交回复
热议问题