Pergunta

Por que a interface Iterator não estender Iterable?

O método iterator() poderia simplesmente voltar this.

É de propósito ou apenas um descuido de designers do Java?

Seria conveniente para ser capaz de usar um for-each loop com iterators assim:

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

onde listSomeObjects() retorna um iterador.

Foi útil?

Solução

Como um iterador geralmente aponta para uma única instância em uma coleção. Iterable implica que se pode obter um iterador de um objeto para percorrer mais de seus elementos -. E não há necessidade de iterar sobre uma única instância, que é o que um iterador representa

Outras dicas

Um iterador é stateful. A idéia é que se você chamar Iterable.iterator() duas vezes você vai ter independentes iterators - para a maioria dos iterables, de qualquer maneira. Que claramente não seria o caso em seu cenário.

Por exemplo, eu geralmente pode escrever:

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

Isso deve imprimir a coleção duas vezes -., Mas com o seu esquema segundo loop sempre terminar instantaneamente

Para o meu $ 0,02, eu concordo completamente que Iterator não deve implementar Iterable, mas acho que o loop for aprimorado deve aceitar qualquer um. Eu acho que todo o "make iterators iteráveis" argumento surge como um trabalho em torno a um defeito na língua.

Toda a razão para a introdução do loop for aprimorado foi que ele "elimina as variáveis ??trabalho penoso e erro propensão de iteradores e índice quando iteração sobre coleções e matrizes" [ 1 ].

Collection<Item> items...

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

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

Por que então esse mesmo argumento se sustenta para iterators?

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

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

Em ambos os casos, as chamadas para hasNext () e próxima () foram removidos, e não há nenhuma referência à iteração no loop interno. Sim, eu entendo que Iterables pode ser a criação de múltiplas iterators reutilizado, mas que tudo acontece fora do loop for: dentro do loop só há sempre um item de uma progressão para a frente em um momento sobre os itens retornados pelo iterador <. / p>

Além disso, permitindo que isso também faria com que seja fácil de usar o loop for para as contagens, o que, como já foi apontado em outros lugares, são análogas às Iterators não Iterables.

Portanto, não fazer Iterator implementar Iterable, mas atualizar o loop for para aceitar qualquer um.

Cheers,

Como foi assinalado por outros, Iterator e Iterable são duas coisas diferentes.

Além disso, implementações Iterator predate reforçada para loops.

Também é trivial para superar esta limitação com um método simples adaptador que se parece com isso quando usado com importações método estático:

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

implementação de exemplo:

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

Em Java 8 adaptar uma Iterator a um Iterable fica mais simples:

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

Como já foi dito, um Iterable pode ser chamado várias vezes, retornando uma nova Iterator em cada chamada; um Iterator é usado apenas uma vez. Então, eles estão relacionados, mas servem a propósitos diferentes. Frustrantemente, porém, o "compacto para" método funciona apenas com um iterable.

O que vou descrever abaixo é uma maneira de ter o melhor dos dois mundos -. Retornando um Iterable (para mais agradável sintaxe), mesmo quando a seqüência subjacente de dados é one-off

O truque é retornar uma implementação anónimo da Iterable que realmente desencadeia o trabalho. Então, ao invés de fazer o trabalho que gera um one-off seqüência e, em seguida, retornar um iterador sobre isso, você retornar um Iterable que, cada vez que é acessado, refaz o trabalho. Isso pode parecer um desperdício, mas muitas vezes você só vai chamar a Iterable uma vez de qualquer maneira, e mesmo se você chamá-lo várias vezes, ele ainda tem a semântica razoáveis ??(ao contrário de um invólucro simples que faz um "look like" Iterator um Iterable, este won' t falhar se usado duas vezes).

Por exemplo, dizer que tenho um DAO que fornece uma série de objetos a partir de um banco de dados, e eu quero dar acesso a essa via um iterador (ex. Para evitar a criação de todos os objetos na memória, se eles não são necessários). Agora eu só poderia retornar um iterador, mas que faz usando o valor retornado em um loop feio. Então, ao invés eu embrulhar tudo em um 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();
            }
        };
    }
}

Este pode então ser usado em código como este:

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

que me permite usar o compacto loop enquanto ainda mantém o uso de memória incremental.

Esta abordagem é "preguiçoso" - o trabalho não é feito quando o Iterable é solicitada, mas só mais tarde, quando os conteúdos são iterada - e você precisa estar ciente das consequências dessa. No exemplo com um DAO que meios iteração sobre os resultados dentro da transação do banco de dados.

Assim, existem várias advertências, mas esta ainda pode ser uma expressão útil em muitos casos.

Incrivelmente, ninguém mais deu esta resposta ainda. Veja como você pode "facilmente" iteração sobre um Iterator usando o novo Java 8 Iterator.forEachRemaining() método:

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

É claro que há uma solução "mais simples" que trabalha com o loop foreach diretamente, envolvendo o Iterator em uma lambda Iterable:

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

Iterator é uma interface que permite que você iterar sobre algo. É uma implementação de mover-se através de uma coleção de algum tipo.

Iterable é uma interface funcional que denota que algo contém um iterador acessível.

Em Java8, isso torna a vida muito fácil ... Se você tem um Iterator mas precisa de um Iterable você pode simplesmente fazer:

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

Isso também funciona no para-loop:

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

Eu também vejo muitos se fazer isso:

public Iterator iterator() {
    return this;
}

Mas isso não significa que seja certo! Este método não seria o que você quer!

O iterator() método deve retornar uma nova iteração começar do zero. Assim, uma necessidade de fazer algo parecido com isto:

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

A questão é: que haveria alguma maneira de fazer uma classe abstrata realizar este? De modo que para obter uma IterableIterator só precisa implementar os dois métodos next () e hasNext ()

Se você veio aqui em busca de uma solução alternativa, você pode usar IteratorIterable . (Disponível para Java 1.6 e acima)

Exemplo de uso (invertendo um 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);
        }
    }
}

impressões

one
two
two
one

Eu concordo com a resposta aceita, mas quero acrescentar a minha própria explicação.

  • Iterator representa o estado da travessia, por exemplo, você pode obter o elemento atual de um iterador e avançar para a próxima.

  • Iterable representa uma coleção que pode ser percorrido, ele pode retornar como muitos iterators como você quer, cada uma representando o seu próprio estado de travessia, um iterador pode estar apontando para o primeiro elemento, enquanto outro pode estar apontando para o 3º elemento.

Seria bom se o Java loop for aceita tanto Iterator e Iterable.

Por uma questão de simplicidade, Iterator e Iterable são dois conceitos distintos, Iterable é simplesmente uma abreviação de "eu posso retornar um Iterator". Eu acho que o código deve ser:

for(Object o : someContainer) {
}

com someContainer instanceof SomeContainer extends Iterable<Object>

Como um aparte: Scala tem um método toIterable () in Iterator. Consulte conversão implícita ou explícita do iterador para iterable

Em uma nota relacionada, você pode encontrar o adaptador IteratorIterable no Apache Commons Collections4 útil. Basta criar uma instância de um iterador, e você tem a iterable correspondente.

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

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

Iterators são stateful, eles têm um "próximo" elemento e tornar-se "exausto" uma vez iterado. Para ver onde o problema está, execute o seguinte código, quantos números são impressos?

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);

Você pode tentar o seguinte exemplo:

List ispresent=new ArrayList();
Iterator iterator=ispresent.iterator();
while(iterator.hasNext())
{
    System.out.println(iterator.next());
}
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top