Hoy hablaremos sobre qué es Iterator en Java y por qué es necesario.
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.
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
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. |
ArrayList
diferencian LinkedList
y HashSet
de 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étodoremove()
(en el caso de colecciones de sólo lectura, por ejemplo)IllegalStateException
, si el métodonext()
aún no ha sido llamado, o siremove()
ya ha sido llamado desde la última llamadanext()
.
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 elementoE
en ;List
boolean hasPrevious()
— regresarátrue
siList
hay 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 llamadanext()
oprevious()
con el elementoe
.
List
que 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
, Set
y Queue
admiten iteradores. For java Map iterator
tambié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 Entry
que 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 WordPart
y 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:
- finalizando
- sufijo
- consola
- raíz
GO TO FULL VERSION