Hoje falaremos sobre o que é Iterator em Java e por que ele é necessário.
Como você provavelmente já sabe, Java possui uma interface Collection maravilhosa que implementa a interface Iterator. Deixe-me fazer uma reserva imediatamente: a interface do iterador não deve ser confundida com o padrão do iterador em Java! E para esclarecer, vamos primeiro dar uma olhada na interface.
Vamos imaginar por um segundo que não exista iterador em Java. Nesse caso, cada um terá que mergulhar nas profundezas das coleções e entender verdadeiramente o que é diferente
Literalmente, “Iterador” pode ser traduzido como “força bruta ” . Ou seja, é uma determinada entidade que pode iterar por todos os elementos da coleção. Além disso, permite fazer isso sem se aprofundar na estrutura interna e na disposição das coleções. |
ArrayList
de LinkedList
e HashSet
de TreeSet
.
Métodos que o Iterador deve implementar
boolean hasNext()
— se ainda houver valores no objeto iterável (atualmente uma Coleção), o método retornará true
, se não houver mais valores false
. E next()
— retorna o próximo elemento da coleção (objeto). Se não houver mais elementos (não houve check hasNext()
e chamamos next()
quando chegamos ao final da coleção), o método lançará NoSuchElementException
. void remove()
- removerá o último elemento obtido pelo next()
. O método pode lançar:
UnsupportedOperationException
, se este iterador não suportar o métodoremove()
(no caso de coleções somente leitura, por exemplo)IllegalStateException
, se o métodonext()
ainda não foi chamado ou seremove()
já foi chamado desde a última chamadanext()
.
for-each
. Sua extensão é ListIterator. Vejamos métodos iteradores de lista java adicionais. Você provavelmente os conhece:
void add(E e)
— insere um elementoE
em ;List
boolean hasPrevious()
— retornarátrue
seList
houver elementos durante a busca reversa;int nextIndex()
— retornará o índice do próximo elemento;E previous()
— retornará o elemento da planilha anterior;int previousIndex()
— retornará o índice do elemento anterior;void set(E e)
- substituirá o elemento retornado pela última chamadanext()
ouprevious()
pelo elementoe
.
List
contendo as linhas de saudações aos alunos:
List<String> list = new ArrayList<>();
list.add("Hello");
list.add("Обучающимся");
list.add("На");
list.add("JavaRush");
Agora obteremos um iterador para ele e imprimiremos todas as linhas contidas no console:
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
Agora haverá um “gargalo”: as coleções Java, como você provavelmente sabe (e se não sabe, descubra), estendem a interface Iterable
, mas isso não significa apenas List
, Set
e Queue
suportam o iterador. For java Map iterator
também é suportado, mas deve ser chamado for Map.entrySet()
:
Map<String, Integer> map = new HashMap<>();
Iterator mapIterator = map.entrySet().iterator();
Então o método next()
retornará um objeto Entry
contendo um par “chave”-“valor”. Então tudo é igual com List
:
while (mapIterator.hasNext()) {
Map.Entry<String, Integer> entry = mapIterator.next();
System.out.println("Key: " + entry.getKey());
System.out.println("Value: " + entry.getValue());
}
Você pensa: “Pare. Estamos falando da interface, e o título do artigo diz “Padrão”. Ou seja, o padrão do iterador é a interface do Iterador? Ou a interface é um padrão? Se esta palavra aparecer pela primeira vez, dou-lhe uma referência: um padrão é um padrão de design, um determinado comportamento que uma classe ou muitas classes interligadas devem aderir. Um iterador em java pode ser implementado para qualquer objeto cuja estrutura interna envolva iteração, e você pode alterar a assinatura dos métodos em discussão. O principal ao implementar um padrão é a lógica que a classe deve seguir. A interface do iterador é uma implementação privada do padrão de mesmo nome, aplicada tanto a estruturas prontas ( List, Set, Queue, Map
), quanto a outras, a critério do programador. Ao estender a interface do Iterator, você implementa um padrão, mas não precisa estender a interface para implementar o padrão. Uma analogia simples: todos os peixes nadam, mas nem tudo que nada é peixe. Como exemplo, decidi pegar... a palavra. Mais especificamente, um substantivo. Consiste em partes: prefixo, raiz, sufixo e finalização. Para partes de uma palavra, criaremos uma interface WordPart
e classes que a estendem 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;
}
}
Então a classe Word
(palavra) conterá partes, e além delas adicionaremos um número inteiro refletindo o número de partes da palavra:
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;
}
Ok, temos quatro construtores sobrecarregados (para simplificar, vamos supor que só podemos ter um sufixo). Um substantivo não pode consistir em um prefixo, portanto, para um construtor com um parâmetro, definiremos a raiz. Agora vamos escrever uma implementação do padrão iterador: WordIterator, substituindo 2 métodos: hasNext()
e 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--;
}
}
}
Tudo o que resta é atribuir o iterador à classe Word
:
public class Word implements Iterable<Word.WordPart> {
…
@Override
public Iterator<WordPart>iterator() {
return new WordIterator(this);
}
…
}
Agora vamos realizar uma análise morfêmica da palavra “rush”:
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());
}
}
}
Observe que em minha implementação do padrão iterador, escolhi a seguinte ordem de saída:
- final
- sufixo
- console
- raiz
GO TO FULL VERSION