JavaRush /Blog Java /Random-ES /Patrón iterador

Patrón iterador

Publicado en el grupo Random-ES
Hoy hablaremos sobre qué es Iterator en Java y por qué es necesario.
Patrón iterador - 1
Como probablemente ya sepa, Java tiene una maravillosa interfaz Collection que implementa la interfaz Iterator. Permítanme hacer una reserva de inmediato: ¡la interfaz del iterador no debe confundirse con el patrón del iterador en Java! Y para aclarar, veamos primero la interfaz.
Literalmente, "Iterador" puede traducirse como "fuerza bruta " . Es decir, es una determinada entidad que puede recorrer en iteración todos los elementos de la colección. Además, le permite hacer esto sin profundizar en la estructura interna y la disposición de las colecciones.
Imaginemos por un segundo que no existe un iterador en Java. En este caso, todos y cada uno tendrán que sumergirse en lo más profundo de las colecciones y comprender verdaderamente en qué se ArrayListdiferencian LinkedListy HashSetde TreeSet.

Métodos que Iterator debe implementar

boolean hasNext()— si todavía quedan valores en el objeto iterable (actualmente una Colección), el método devolverá true, si no hay más valores false. E next()— devuelve el siguiente elemento de la colección (objeto). Si no hay más elementos (no hubo check hasNext()y llamamos next()cuando llegamos al final de la colección), el método arrojará NoSuchElementException. void remove()- eliminará el elemento obtenido por última vez por next(). El método puede arrojar:
  • UnsupportedOperationException, si este iterador no soporta el método remove()(en el caso de colecciones de sólo lectura, por ejemplo)
  • IllegalStateException, si el método next()aún no ha sido llamado, o si remove()ya ha sido llamado desde la última llamada next().
Entonces, el iterador de Lista es la implementación más común. El iterador va desde el principio de la colección hasta su final: mira para ver si el siguiente elemento está presente y lo devuelve si lo hay. Se construye un ciclo sobre la base de este sencillo algoritmo for-each. Su extensión es ListIterator. Veamos métodos adicionales de iterador de listas de Java. Probablemente los conozcas:
  • void add(E e)— inserta un elemento Een ;List
  • boolean hasPrevious()— regresará truesi Listhay elementos durante la búsqueda inversa;
  • int nextIndex()— devolverá el índice del siguiente elemento;
  • E previous()— devolverá el elemento de la hoja anterior;
  • int previousIndex()— devolverá el índice del elemento anterior;
  • void set(E e)- reemplazará el elemento devuelto por la última llamada next()o previous()con el elemento e.
Veamos un pequeño ejemplo. Creemos un archivo Listque contenga las líneas de saludo a los estudiantes:
List<String> list = new ArrayList<>();
list.add("Hola");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
Ahora obtendremos un iterador e imprimiremos todas las líneas contenidas en la consola:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
    System.out.println(iterator.next());
}
Ahora habrá un "cuello de botella": las colecciones de Java, como probablemente sepa (y si no lo sabe, descúbralo), amplían la interfaz Iterable, pero esto no significa solo eso List, Sety Queueadmiten iteradores. For java Map iteratortambién se admite, pero debe solicitarse Map.entrySet():
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
Luego, el método next() devolverá un objeto Entryque contiene un par "clave"-"valor". Entonces todo es igual con List:
while (mapIterator.hasNext()) {
    Map.Entry<String, Integer> entry = mapIterator.next();
    System.out.println("Key: " + entry.getKey());
    System.out.println("Value: " + entry.getValue());
}
Piensas: “Para. Estamos hablando de la interfaz y el título del artículo dice “Patrón”. Es decir, ¿el patrón del iterador es la interfaz del iterador? ¿O la interfaz es un patrón? Si esta palabra aparece por primera vez, les doy una referencia: un patrón es un patrón de diseño, un determinado comportamiento al que debe adherirse una clase o muchas clases interconectadas. Se puede implementar un iterador en Java para cualquier objeto cuya estructura interna implique iterar, y se puede cambiar la firma de los métodos que se analizan. Lo principal a la hora de implementar un patrón es la lógica a la que debe adherirse la clase. La interfaz del iterador es una implementación privada del patrón del mismo nombre, aplicada tanto a estructuras ya preparadas ( List, Set, Queue, Map) como a otras, a discreción del programador. Al extender la interfaz Iterator, implementa un patrón, pero no necesita extender la interfaz para implementar el patrón. Una analogía sencilla: todos los peces nadan, pero no todo lo que nada es un pez. Como ejemplo, decidí tomar... la palabra. Más específicamente, un sustantivo. Consta de partes: prefijo, raíz, sufijo y terminación. Para partes de una palabra, crearemos una interfaz WordParty clases que la amplían Prefix, Root, Suffix и Ending:
interface WordPart {
    String getWordPart();
}

static class Root implements WordPart {

    private String part;

    public Root(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Prefix implements WordPart {

    private String part;

    public Prefix(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Suffix implements WordPart {

    private String part;

    public Suffix(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}

static class Ending implements WordPart {

    private String part;

    public Ending(String part) {
        this.part = part;
    }

    @Override
    public String getWordPart() {
        return part;
    }
}
Luego, la clase Word(palabra) contendrá partes y, además de ellas, agregaremos un número entero que refleje el número de partes de la palabra:
public class Word {

    private Root root;
    private Prefix prefix;
    private Suffix suffix;
    private Ending ending;
    private int partCount;

    public Word(Root root, Prefix prefix, Suffix suffix, Ending ending) {
        this.root = root;
        this.prefix = prefix;
        this.suffix = suffix;
        this.ending = ending;
        this.partCount = 4;
    }

    public Word(Root root, Prefix prefix, Suffix suffix) {
        this.root = root;
        this.prefix = prefix;
        this.suffix = suffix;
        this.partCount = 3;

    }

    public Word(Root root, Prefix prefix) {
        this.root = root;
        this.prefix = prefix;
        this.partCount = 2;
    }

    public Word(Root root) {
        this.root = root;
        this.partCount = 1;
    }

    public Root getRoot() {
        return root;
    }

    public Prefix getPrefix() {
        return prefix;
    }

    public Suffix getSuffix() {
        return suffix;
    }

    public Ending getEnding() {
        return ending;
    }

    public int getPartCount() {
        return partCount;
    }

    public boolean hasRoot() {
        return this.root != null;
    }

    public boolean hasPrefix() {
        return this.prefix != null;
    }

    public boolean hasSuffix() {
        return this.suffix != null;
    }

    public boolean hasEnding() {
        return this.ending != null;
    }
Bien, tenemos cuatro constructores sobrecargados (para simplificar, supongamos que solo podemos tener un sufijo). Un sustantivo no puede constar de un prefijo, por lo que para un constructor con un parámetro estableceremos la raíz. Ahora escribamos una implementación del patrón iterador: WordIterator, anulando 2 métodos: hasNext()y next():
public class WordIterator implements Iterator<Word.WordPart> {

    private Word word;
    private int wordPartsCount;

    public WordIterator(Word word) {
        this.word = word;
        this.wordPartsCount = word.getPartCount();
    }

    @Override
    public boolean hasNext() {
        if (wordPartsCount == 4) {
            return word.hasPrefix() || word.hasRoot() || word.hasSuffix() || word.hasEnding();
        } else if (wordPartsCount == 3) {
            return word.hasPrefix() || word.hasRoot() || word.hasSuffix();
        } else if (wordPartsCount == 2) {
            return word.hasPrefix() || word.hasRoot();
        } else if (wordPartsCount == 1) {
            return word.hasRoot();
        }
        return false;
    }

    @Override
    public Word.WordPart next() throws NoSuchElementException {
        if (wordPartsCount <= 0) {
            throw new NoSuchElementException("No more elements in this word!");
        }

        try {
            if (wordPartsCount == 4) {
                return word.getEnding();
            }
            if (wordPartsCount == 3) {
                return word.getSuffix();
            }
            if (wordPartsCount == 2) {
                return word.getPrefix();
            }
            return word.getRoot();
        } finally {
            wordPartsCount--;
        }
    }
}
Todo lo que queda es asignar el iterador a la clase Word:
public class Word implements Iterable<Word.WordPart> {@Override
	public Iterator<WordPart>iterator() {
    		return new WordIterator(this);
	}}
Ahora realicemos un análisis morfémico de la palabra "prisa":
public class Main {
    public static void main(String[] args) {
        Word.Root root = new Word.Root("беж");
        Word.Prefix prefix = new Word.Prefix("пере");
        Word.Suffix suffix = new Word.Suffix("к");
        Word.Ending ending = new Word.Ending("a");

        Word word = new Word(root, prefix, suffix, ending);

        Iterator wordIterator = word.iterator();
        while (wordIterator.hasNext()) {
            Word.WordPart part = (Word.WordPart) wordIterator.next();
            System.out.println(part.getClass() + ": " + part.getWordPart());
        }
    }
}
Tenga en cuenta que en mi implementación del patrón iterador, elegí el siguiente orden de salida:
  1. finalizando
  2. sufijo
  3. consola
  4. raíz
Al diseñar su propio iterador, puede especificar el algoritmo de iteración que desee. ¡Buena suerte en tus estudios!
Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION