1. Histórico de como os iteradores surgiram

Você já está familiarizado com HashSet. Se você realmente investigou, além de apenas ler uma lição, deveria ter feito esta pergunta:

Como faço para exibir uma lista de todos os elementos HashSet na tela? Afinal, a interface não tem get()e set()métodos!

E HashSetnão está sozinho nessa limitação. Além de HashSet, existem muitas outras coleções que não permitem que os elementos sejam recuperados pelo índice, pois os elementos não possuem ordem definida.

Ao longo dos anos, os programadores inventaram muitas estruturas de dados complexas, como gráficos e árvores. Ou listas de listas.

Muitos contêineres alteram a ordem de seus elementos quando novos elementos são adicionados ou elementos existentes são removidos. Por exemplo, uma lista armazena elementos em uma determinada ordem e, quando um novo elemento é adicionado, quase sempre é inserido no meio da lista.

E também temos situações em que há um contêiner que armazena elementos, mas não em uma ordem fixa.

Agora, digamos que queremos copiar todos os elementos de tal coleção para um array ou lista. Precisamos obter todos os elementos. Não nos importamos com a ordem em que iteramos sobre os elementos — o importante é não iterar sobre os mesmos elementos mais de uma vez. Como fazemos isso?


2. Iterador para uma coleção

Iteradores foram propostos como uma solução para o problema acima.

Um iterador é um objeto especial associado a uma coleção, que ajuda a percorrer todos os elementos da coleção sem repetir nenhum.

Você pode usar o seguinte código para obter um iterador para qualquer coleção:

Iterator<Type> it = name.iterator();

Onde nameé o nome da variável da coleção, Typeé o tipo dos elementos da coleção, iterator()é um dos métodos da coleção e ité o nome da variável do iterador.

Um objeto iterador tem 3 métodos:

Método Descrição
Type next()
Retorna o próximo elemento da coleção
boolean hasNext()
Verifica se existem elementos que ainda não foram percorridos
void remove()
Remove o elemento atual da coleção

nextInt)Esses métodos são um pouco semelhantes aos métodos e da classe Scanner hasNextInt().

O next()método retorna o próximo elemento da coleção da qual obtivemos o iterador.

O hasNext()método verifica se a coleção possui elementos adicionais que o iterador ainda não retornou.

Veja como exibir todos os elementos de um HashSet:

Código Notas
HashSet<String> set = new HashSet<String>();

set.add("Hallo");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
Crie um HashSetobjeto que armazene Stringelementos.


Adicionamos saudações em vários idiomas à setvariável.




Obtenha um objeto iterador para o setconjunto.
Desde que ainda existam elementos

Obtenha o próximo elemento
Exiba o elemento na tela


3. For-eachloop

A principal desvantagem de um iterador é que seu código se torna mais complicado do que usar um forloop.

Para comparar, vamos exibir uma lista usando um forloop e também usando um iterador:

Iterador para loop
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

Sim, é muito melhor percorrer os elementos de um ArrayListusando um loop — tudo fica mais curto.

Mas os criadores do Java novamente decidiram jogar um pouco de açúcar em nós. Felizmente para nós, era açúcar sintático .

Eles deram a Java um novo tipo de loop e o chamaram de for-eachloop. É assim que fica em geral:

for(Type name:collection)

Onde collectioné o nome da variável da coleção, Typeé o tipo dos elementos na coleção e nameé o nome de uma variável que obtém o próximo valor da coleção em cada iteração do loop.

Esse tipo de loop itera por todos os elementos de uma coleção usando um iterador implícito. É assim que realmente funciona:

Para cada loop O que o compilador vê: Loop com um iterador
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

Quando o compilador encontra um for-eachloop em seu código, ele simplesmente o substitui pelo código à direita: ele adiciona uma chamada para obter um iterador junto com qualquer outra chamada de método ausente.

Os programadores adoram o for-eachloop e quase sempre o usam quando precisam iterar sobre todos os elementos de uma coleção.

Mesmo a iteração em uma ArrayListlista usando um for-eachloop parece mais curta:

Para cada loop para loop
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


4. Removendo um elemento em um for-eachloop

O for-eachloop tem uma desvantagem: não pode remover os elementos corretamente. Se você escrever um código como este, receberá um erro.

Código Observação
ArrayList<String> list = new ArrayList<String>();

list.add("Hallo");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












A operação de remoção irá gerar um erro!

Este é um código muito bom e compreensível, mas não funcionará.

Importante!

Você não pode alterar uma coleção enquanto a percorre com um iterador.

Existem três maneiras de contornar essa limitação.

1. Use um tipo diferente de loop

When traversing an ArrayList collection, você pode usar um loop comum com uma ivariável de contador.

Código
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

No entanto, esta opção não é adequada para HashSete HashMapcoleções

2. Use um iterador explícito

Você pode usar um iterador explicitamente e chamar seu remove()método.

Versão que funciona Versão que não funciona
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}

for (String str: list) { if (str.equals("Hello")) list.remove(str); }

Observe que chamamos o remove()método no objeto iterador! O iterador está ciente de que o item foi removido e pode lidar com a situação corretamente.

3. Use uma cópia da coleção

Você também pode criar uma cópia da coleção e, em seguida, usar a cópia em um for-eachloop e excluir elementos da coleção original.

Código Observação
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Criar uma cópia de uma coleção é super fácil.



O loop usa o iterador para a cópia da coleção.
Os elementos são removidos da listcoleção.

A coleção é copiada rapidamente, pois os próprios elementos não são duplicados. Em vez disso, a nova coleção armazena referências aos elementos que já existem na coleção antiga.