Pregunta

¿Por qué la interfaz Iterator no se extiende Iterable?

El método iterator() podría simplemente devolver this.

¿Es a propósito o simplemente una supervisión de los diseñadores de Java?

Sería conveniente poder usar un ciclo for-each con iteradores como este:

for(Object o : someContainer.listSomeObjects()) {
    ....
}

donde listSomeObjects() devuelve un iterador.

¿Fue útil?

Solución

Porque un iterador generalmente apunta a una sola instancia en una colección. Iterable implica que uno puede obtener un iterador de un objeto para atravesar sus elementos, y no hay necesidad de iterar sobre una sola instancia, que es lo que representa un iterador.

Otros consejos

Un iterador tiene estado. La idea es que si llama a Iterable.iterator() dos veces obtendrá iteradores independientes , de todos modos para la mayoría de los iterables. Claramente, ese no sería el caso en su escenario.

Por ejemplo, generalmente puedo escribir:

public void iterateOver(Iterable<String> strings)
{
    for (String x : strings)
    {
         System.out.println(x);
    }
    for (String x : strings)
    {
         System.out.println(x);
    }
}

Eso debería imprimir la colección dos veces, pero con su esquema, el segundo ciclo siempre finalizaría instantáneamente.

Para mis $ 0.02, estoy completamente de acuerdo en que Iterator no debe implementar Iterable, pero creo que el bucle mejorado también debería aceptarlo. Creo que todo & Quot; hacer iteradores iterables & Quot; el argumento surge como una solución a un defecto en el lenguaje.

Todo el motivo de la introducción del bucle for mejorado fue que & "elimina el trabajo pesado y la propensión a errores de los iteradores y las variables de índice al iterar sobre colecciones y matrices &"; [ 1 ].

Collection<Item> items...

for (Iterator<Item> iter = items.iterator(); iter.hasNext(); ) {
    Item item = iter.next();
    ...
}

for (Item item : items) {
    ...
}

¿Por qué entonces este mismo argumento no es válido para los iteradores?

Iterator<Iter> iter...
..
while (iter.hasNext()) {
    Item item = iter.next();
    ...
}

for (Item item : iter) {
    ...
}

En ambos casos, las llamadas a hasNext () y next () se han eliminado, y no hay ninguna referencia al iterador en el bucle interno. Sí, entiendo que los Iterables se pueden reutilizar para crear múltiples iteradores, pero que todo sucede fuera del ciclo for: dentro del ciclo solo hay una progresión hacia adelante un elemento a la vez sobre los elementos devueltos por el iterador.

Además, permitir esto también facilitaría el uso del bucle for para Enumeraciones, que, como se ha señalado en otra parte, son análogos a Iteradores no Iterables.

Por lo tanto, no haga que Iterator implemente Iterable, sino que actualice el bucle for para aceptar tampoco.

Saludos,

Como lo señalaron otros, Iterator y < a href = "http://docs.oracle.com/javase/7/docs/api/java/lang/Iterable.html"> Iterable son dos cosas diferentes.

Además, las implementaciones <=> son anteriores a los bucles mejoradas.

También es trivial superar esta limitación con un método de adaptador simple que se ve así cuando se usa con importaciones de métodos estáticos:

for (String line : in(lines)) {
  System.out.println(line);
}

Implementación de muestra:

  /**
   * 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();
  }

En Java 8, adaptar un <=> a un <=> se vuelve más simple:

for (String s : (Iterable<String>) () -> iterator) {

Como han dicho otros, un Iterable se puede llamar varias veces, devolviendo un nuevo iterador en cada llamada; un iterador se usa solo una vez. Entonces están relacionados, pero tienen diferentes propósitos. Frustrantemente, sin embargo, el & Quot; compacto para & Quot; El método solo funciona con un iterable.

Lo que describiré a continuación es una forma de tener lo mejor de ambos mundos: devolver un Iterable (para una sintaxis más agradable) incluso cuando la secuencia de datos subyacente es única.

El truco consiste en devolver una implementación anónima de Iterable que realmente active el trabajo. Entonces, en lugar de hacer el trabajo que genera una secuencia única y luego devolver un iterador sobre eso, devuelve un Iterable que, cada vez que se accede, vuelve a hacer el trabajo. Puede parecer un desperdicio, pero a menudo solo llamarás al Iterable de todos modos, e incluso si lo llamas varias veces, todavía tiene una semántica razonable (a diferencia de un simple contenedor que hace que un Iterator & Parezca " un Iterable, esto no fallará si se usa dos veces).

Por ejemplo, supongamos que tengo un DAO que proporciona una serie de objetos de una base de datos y quiero proporcionar acceso a eso a través de un iterador (por ejemplo, para evitar crear todos los objetos en la memoria si no son necesarios). Ahora podría devolver un iterador, pero eso hace que usar el valor devuelto en un bucle sea feo. Entonces, en cambio, envuelvo todo en un 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();
            }
        };
    }
}

esto se puede usar en un código como este:

class DaoUser {
    private MetricDao dao;
    for (Metric existing : dao.loadAll()) {
        // do stuff here...
    }
}

que me permite usar el bucle compacto para mantener el uso de memoria incremental.

Este enfoque es " perezoso " - el trabajo no se realiza cuando se solicita el Iterable, sino solo más tarde cuando los contenidos se repiten, y debe ser consciente de las consecuencias de eso. En el ejemplo con un DAO que significa iterar sobre los resultados dentro de la transacción de la base de datos.

Así que hay varias advertencias, pero esto puede ser una expresión útil en muchos casos.

Increíblemente, nadie más ha dado esta respuesta todavía. Así es como puedes & Quot; fácilmente & Quot; iterar sobre un Iterator utilizando el nuevo Java 8 Iterator.forEachRemaining() método:

Iterator<String> it = ...
it.forEachRemaining(System.out::println);

Por supuesto, hay un " más simple " solución que funciona con el bucle foreach directamente, envolviendo el Iterable en una <=> lambda:

for (String s : (Iterable<String>) () -> it)
    System.out.println(s);

Iterator es una interfaz que le permite iterar sobre algo. Es una implementación de moverse a través de una colección de algún tipo.

Iterable es una interfaz funcional que denota que algo contiene un iterador accesible.

En Java8, esto hace la vida bastante fácil ... Si tiene un <=> pero necesita un <=> simplemente puede hacer:

Iterator<T> someIterator;
Iterable<T> = ()->someIterator;

Esto también funciona en el ciclo for:

for (T item : ()->someIterator){
    //doSomething with item
}

También veo a muchos haciendo esto:

public Iterator iterator() {
    return this;
}

¡Pero eso no lo hace correcto! ¡Este método no sería lo que quieres!

Se supone que el método iterator() devuelve un nuevo iterador a partir de cero. Entonces uno necesita hacer algo como esto:

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() {
    // ...
  }
}

La pregunta es: ¿habría alguna forma de hacer que una clase abstracta realice esto? Para obtener un IterableIterator solo es necesario implementar los dos métodos next () y hasNext ()

Si vino aquí en busca de una solución alternativa, puede usar IteratorIterable . (disponible para Java 1.6 y superior)

Ejemplo de uso (invertir un vector).

import java.util.Vector;
import org.apache.commons.collections4.iterators.IteratorIterable;
import org.apache.commons.collections4.iterators.ReverseListIterator;
public class Test {
    public static void main(String ... args) {
        Vector<String> vs = new Vector<String>();
        vs.add("one");
        vs.add("two");
        for ( String s: vs ) {
            System.out.println(s);
        }
        Iterable<String> is
            = new IteratorIterable(new ReverseListIterator(vs));
        for ( String s: is ) {
            System.out.println(s);
        }
    }
}

impresiones

one
two
two
one

Estoy de acuerdo con la respuesta aceptada, pero quiero agregar mi propia explicación.

  • Iterator representa el estado de desplazamiento, por ejemplo, puede obtener el elemento actual de un iterador y avanzar al siguiente.

  • Iterable representa una colección que podría atravesarse, puede devolver tantos iteradores como desee, cada uno representando su propio estado de desplazamiento, un iterador puede estar apuntando al primer elemento, mientras que otro puede estar apuntando al 3er elemento.

Sería bueno si Java for loop acepta tanto Iterator como Iterable.

En aras de la simplicidad, Iterator e Iterable son dos conceptos distintos, Iterable es simplemente una abreviatura de & "; Puedo devolver un Iterator &"; Creo que su código debería ser:

for(Object o : someContainer) {
}

con alguna instancia de Contenedor de SomeContainer extends Iterable<Object>

Como comentario aparte: Scala tiene un método toIterable () en Iterator. Consulte scala conversión implícita o explícita de iterador a iterable

En una nota relacionada, puede encontrar útil el adaptador IteratorIterable en Apache Commons Collections4. Simplemente cree una instancia desde un iterador y tendrá el iterable correspondiente.

https: // commons.apache.org/proper/commons-collections/apidocs/org/apache/commons/collections4/iterators/IteratorIterable.html

ID: org.apache.commons: commons-collections4: 4.0

Los iteradores tienen estado, tienen un " next " elemento y convertirse en " agotado " Una vez iterado. Para ver dónde está el problema, ejecute el siguiente código, ¿cuántos números se imprimen?

Iterator<Integer> iterator = Arrays.asList(1,2,3).iterator();
Iterable<Integer> myIterable = ()->iterator;
for(Integer i : myIterable) System.out.print(i);
System.out.println();
for(Integer i : myIterable) System.out.print(i);

Puedes probar el siguiente ejemplo:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top