Podemos escrever nosso próprio iterador em Java?

104

Se eu tiver uma lista contendo [alice, bob, abigail, charlie]e quiser escrever um iterador de forma que itere sobre os elementos que começam com 'a', posso escrever o meu próprio? Como eu posso fazer isso ?

Fénix
fonte
4
Você pode. Você deve implementar a interface Iterator.
gd1
claro, é apenas uma interface normal. Proxying java.util para um impl JDO. envolve muitos iteradores personalizados.
bestsss
codereview.stackexchange.com/questions/48109/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respostas:

48

Certo. Um iterador é apenas uma implementação da java.util.Iteratorinterface. Se você estiver usando um objeto iterável existente (digamos, a LinkedList) de java.util, você precisará subclassificá-lo e sobrescrever sua iteratorfunção para que você retorne a sua, ou fornecer um meio de agrupar um iterador padrão em sua Iteratorinstância especial (que tem a vantagem de ser mais amplamente utilizado), etc.

TJ Crowder
fonte
8
boa resposta .... +1 No entanto, você não é forçado a subclasse LinkedList. Você pode escrever um CustomIterator que é instanciado com o novo CustomIterator (somelist), uma vez que as interfaces não dizem nada sobre construtores.
gd1
1
@Giacomo: Isso é o que eu quis dizer com "... ou fornecer um meio de agrupar um iterador padrão em sua Iteratorinstância especial ..." (e obrigado). :-)
TJ Crowder
196

A melhor opção reutilizável é implementar a interface Iterable e substituir o iterador do método ().

Aqui está um exemplo de uma classe como ArrayList implementando a interface, na qual você substitui o método Iterator ().

import java.util.Iterator;

public class SOList<Type> implements Iterable<Type> {

    private Type[] arrayList;
    private int currentSize;

    public SOList(Type[] newArray) {
        this.arrayList = newArray;
        this.currentSize = arrayList.length;
    }

    @Override
    public Iterator<Type> iterator() {
        Iterator<Type> it = new Iterator<Type>() {

            private int currentIndex = 0;

            @Override
            public boolean hasNext() {
                return currentIndex < currentSize && arrayList[currentIndex] != null;
            }

            @Override
            public Type next() {
                return arrayList[currentIndex++];
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
        return it;
    }
}

Esta classe implementa a interface Iterable usando Genéricos . Considerando que você tem elementos para o array, você poderá obter uma instância de um Iterator, que é a instância necessária usada pelo loop "foreach", por exemplo.

Você pode simplesmente criar uma instância anônima do iterador sem criar estendendo Iterator e aproveitar o valor de currentSize para verificar até onde você pode navegar sobre a matriz (digamos que você criou uma matriz com capacidade de 10, mas você tem apenas 2 elementos em 0 e 1). A instância terá seu contador proprietário de onde está e tudo que você precisa fazer é brincar com hasNext (), que verifica se o valor atual não é nulo, e o next (), que retornará a instância de seu currentIndex. Abaixo está um exemplo de uso desta API ...

public static void main(String[] args) {
    // create an array of type Integer
    Integer[] numbers = new Integer[]{1, 2, 3, 4, 5};

    // create your list and hold the values.
    SOList<Integer> stackOverflowList = new SOList<Integer>(numbers);

    // Since our class SOList is an instance of Iterable, then we can use it on a foreach loop
    for(Integer num : stackOverflowList) {
        System.out.print(num);
    }

    // creating an array of Strings
    String[] languages = new String[]{"C", "C++", "Java", "Python", "Scala"};

    // create your list and hold the values using the same list implementation.
    SOList<String> languagesList = new SOList<String>(languages);

    System.out.println("");
    // Since our class SOList is an instance of Iterable, then we can use it on a foreach loop
    for(String lang : languagesList) {
        System.out.println(lang);
    }
}
// will print "12345
//C
//C++
//Java
//Python
//Scala

Se desejar, você também pode iterar sobre ele usando a instância Iterator:

// navigating the iterator
while (allNumbers.hasNext()) {
    Integer value = allNumbers.next();
    if (allNumbers.hasNext()) {
        System.out.print(value + ", ");
    } else {
        System.out.print(value);
    }
} 
// will print 1, 2, 3, 4, 5

A documentação do foreach está localizada em http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html . Você pode dar uma olhada em uma implementação mais completa em meu código de prática pessoal do Google .

Agora, para obter os efeitos do que você precisa, acho que você precisa conectar um conceito de filtro no Iterator ... Como o iterador depende dos próximos valores, seria difícil retornar true em hasNext () e, em seguida, filtre a implementação next () com um valor que não comece com um caractere "a", por exemplo. Acho que você precisa brincar com um Interator secundário com base em uma lista filtrada com os valores com o filtro fornecido.

Marcello de Sales
fonte
14
for instance, isso é um trocadilho?
n611x007
4
30 outras pessoas não acharam isso um trocadilho :)
Marcello de Sales
2
É uma boa prática lançar exceção de operação sem suporte de nossos métodos implementados. Acho que é uma boa ideia lançar uma exceção de operação não suportada do método remove ()!
darshan
2
Desculpe @darshan, mas esta solução diz respeito a "como escrever iteradores" ... Se o foco fosse "escrever código perfeitamente escrito", isso estaria lá!
Marcello de Sales de
não está claro porque a verificação 'arrayList [currentIndex]! = null' é necessária dentro de hasNext (). alguém pode explicar.
Bhushan Karmarkar
12

Bom exemplo de Iterable para calcular fatorial

FactorialIterable fi = new FactorialIterable(10);
Iterator<Integer> iterator = fi.iterator();
while (iterator.hasNext()){
     System.out.println(iterator.next());
}

Código curto para Java 1.8

new FactorialIterable(5).forEach(System.out::println);

Classe Iterable Customizada

public class FactorialIterable implements Iterable<Integer> {

    private final FactorialIterator factorialIterator;

    public FactorialIterable(Integer value) {
        factorialIterator = new FactorialIterator(value);
    }

    @Override
    public Iterator<Integer> iterator() {
        return factorialIterator;
    }

    @Override
    public void forEach(Consumer<? super Integer> action) {
        Objects.requireNonNull(action);
        Integer last = 0;
        for (Integer t : this) {
            last = t;
        }
        action.accept(last);
    }

}

Classe Iterator Customizado

public class FactorialIterator implements Iterator<Integer> {

    private final Integer mNumber;
    private Integer mPosition;
    private Integer mFactorial;


    public FactorialIterator(Integer number) {
        this.mNumber = number;
        this.mPosition = 1;
        this.mFactorial = 1;
    }

    @Override
    public boolean hasNext() {
        return mPosition <= mNumber;
    }

    @Override
    public Integer next() {
        if (!hasNext())
            return 0;

        mFactorial = mFactorial * mPosition;

        mPosition++;

        return  mFactorial;
    }
}
Vahe Gharibyan
fonte
8

Este é o código completo para escrever um iterador de forma que itere sobre os elementos que começam com 'a':

import java.util.Iterator;

public class AppDemo {

    public static void main(String args[]) {

        Bag<String> bag1 = new Bag<>();

        bag1.add("alice");
        bag1.add("bob"); 
        bag1.add("abigail");
        bag1.add("charlie"); 

        for (Iterator<String> it1 = bag1.iterator(); it1.hasNext();) {

            String s = it1.next();
            if (s != null)
                System.out.println(s); 
        }
    }
}

Classe Iterator Customizado

import java.util.ArrayList;
import java.util.Iterator;

public class Bag<T> {

    private ArrayList<T> data;

    public Bag() {

        data = new ArrayList<>();
    }

    public void add(T e) {

        data.add(e); 
    }

    public Iterator<T> iterator() {

        return new BagIterator();
    }

    public class BagIterator<T> implements Iterator<T> {

        private int index; 
        private String str;

        public BagIterator() {

            index = 0;
        }

        @Override
        public boolean hasNext() {

             return index < data.size();  
        }

        @Override
        public T next() {

            str = (String) data.get(index); 
            if (str.startsWith("a"))
                return (T) data.get(index++); 
            index++; 
            return null; 
        }
    } 
}
elvis
fonte
5

Você pode implementar seu próprio Iterator. Seu iterador pode ser construído para envolver o Iterador retornado pela Lista, ou você pode manter um cursor e usar o método get (int index) de List. Você só precisa adicionar lógica ao próximo método de seu Iterator E ao método hasNext para levar em consideração seus critérios de filtragem. Você também terá que decidir se o seu iterador suportará a operação de remoção.

ditkin
fonte
1

Aqui está a resposta completa para a pergunta.

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;

class ListIterator implements Iterator<String>{
    List<String> list;
    int pos = 0;

    public ListIterator(List<String> list) {
        this.list = list;
    }

    @Override
    public boolean hasNext() {
        while(pos < list.size()){
            if (list.get(pos).startsWith("a"))
                return true;
            pos++;
        }
        return false;

    }

    @Override
    public String next() {
        if (hasNext())
            return list.get(pos++);
        throw new NoSuchElementException();
    }
}

public class IteratorTest {

    public static void main(String[] args) {
        List<String> list = Arrays.asList("alice", "bob", "abigail", "charlie");
        ListIterator itr = new ListIterator(list);

        while(itr.hasNext())
            System.out.println(itr.next()); // prints alice, abigail
    }
}
  • ListIterator é o iterador para a matriz que retorna os elementos que começam com 'a'.
  • Não há necessidade de implementar uma interface Iterable. Mas essa é uma possibilidade.
  • Não há necessidade de implementar isso genericamente.
  • Ele satisfaz totalmente o contrato para hasNext () e next (). ou seja, se hasNext () diz que ainda há elementos, next () retornará esses elementos. E se hasNext () não diz mais elementos, ele retorna uma NoSuchElementExceptionexceção válida .
Apadana
fonte