¿Podemos escribir nuestro propio iterador en Java?

104

Si tengo una lista que contiene [alice, bob, abigail, charlie]y quiero escribir un iterador de modo que repita los elementos que comienzan con 'a', ¿puedo escribir el mío propio? Cómo puedo hacer eso ?

fénix
fuente
4
Usted puede. Tienes que implementar la interfaz Iterator.
gd1
seguro, es solo una interfaz normal. Proxying java.util para un impl de JDO. implica una gran cantidad de iteradores personalizados.
bestsss
codereview.stackexchange.com/questions/48109/…
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

48

Por supuesto. Un iterador es solo una implementación de la java.util.Iteratorinterfaz. Si está utilizando un objeto iterable existente (digamos, a LinkedList) de java.util, deberá subclasificarlo y anular su iteratorfunción para que devuelva la suya propia, o proporcionar un medio para envolver un iterador estándar en su Iteratorinstancia especial (que tiene la ventaja de ser más ampliamente utilizado), etc.

TJ Crowder
fuente
8
buena respuesta .... +1 Sin embargo, no está obligado a subclase LinkedList. Puede escribir un CustomIterator que se instancia con el nuevo CustomIterator (somelist), ya que las interfaces no dicen nada sobre los constructores.
gd1
1
@Giacomo: Eso es lo que quise decir con "... o proporcionar un medio para envolver un iterador estándar en su Iteratorinstancia especial ..." (y gracias). :-)
TJ Crowder
196

La mejor opción reutilizable es implementar la interfaz Iterable y anular el método iterator ().

Aquí hay un ejemplo de una clase similar a ArrayList que implementa la interfaz, en la que anula el 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 clase implementa la interfaz Iterable usando Generics . Teniendo en cuenta que tiene elementos en la matriz, podrá obtener una instancia de un Iterador, que es la instancia necesaria utilizada por el bucle "foreach", por ejemplo.

Puede simplemente crear una instancia anónima del iterador sin crear un iterador extendido y aprovechar el valor de currentSize para verificar hasta dónde puede navegar sobre la matriz (digamos que creó una matriz con capacidad de 10, pero solo tiene 2 elementos en 0 y 1). La instancia tendrá su contador de propietario de dónde está y todo lo que necesita hacer es jugar con hasNext (), que verifica si el valor actual no es nulo, y next (), que devolverá la instancia de su currentIndex. A continuación se muestra un ejemplo del uso de esta 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

Si lo desea, también puede iterar sobre él usando la instancia de 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

La documentación de foreach se encuentra en http://download.oracle.com/javase/1,5.0/docs/guide/language/foreach.html . Puede echar un vistazo a una implementación más completa en mi código de Google de práctica personal .

Ahora, para obtener los efectos de lo que necesita, creo que debe conectar un concepto de filtro en el Iterador ... Dado que el iterador depende de los siguientes valores, sería difícil devolver verdadero en hasNext (), y luego filtrar la implementación next () con un valor que no comience con un carácter "a", por ejemplo. Creo que necesitas jugar con un Interator secundario basado en una lista filtrada con los valores con el filtro dado.

Marcello de Sales
fuente
14
for instance, ¿es un juego de palabras?
n611x007
4
Otras 30 personas no pensaron que fuera un juego de palabras :)
Marcello de Sales
2
Es una buena práctica lanzar una excepción de operación no admitida de nuestros métodos implementados. ¡Creo que es una buena idea lanzar una excepción de operación no admitida del método remove ()!
darshan
2
Lo siento @darshan, pero esta solución tiene que ver con "cómo escribir iteradores" ... Si el enfoque fuera "escribir código perfectamente escrito", ¡eso estaría ahí!
Marcello de Sales
no está claro por qué se requiere la verificación 'arrayList [currentIndex]! = null' dentro de hasNext (). alguien puede explicar por favor.
Bhushan Karmarkar
12

Buen ejemplo de Iterable para calcular factorial

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

Código corto para Java 1.8

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

Clase Iterable personalizada

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

}

Clase de iterador personalizado

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
fuente
8

Este es el código completo para escribir un iterador de modo que repita los elementos que comienzan con '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); 
        }
    }
}

Clase de iterador personalizado

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
fuente
5

Puede implementar su propio iterador. Su iterador podría construirse para envolver el Iterador devuelto por la Lista, o podría mantener un cursor y usar el método get (índice int) de la Lista. Solo tiene que agregar lógica al siguiente método de su Iterador Y al método hasNext para tener en cuenta sus criterios de filtrado. También tendrá que decidir si su iterador admitirá la operación de eliminación.

ditkin
fuente
1

Aquí está la respuesta completa a la pregunta.

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 es el iterador de la matriz que devuelve los elementos que comienzan con 'a'.
  • No es necesario implementar una interfaz iterable. Pero esa es una posibilidad.
  • No es necesario implementar esto de forma genérica.
  • Satisface completamente el contrato para hasNext () y next (). es decir, si hasNext () dice que todavía hay elementos, next () devolverá esos elementos. Y si hasNext () dice que no hay más elementos, devuelve una NoSuchElementExceptionexcepción válida .
apadana
fuente