Java: melhor maneira de iterar por meio de uma coleção (aqui ArrayList)

98

Hoje eu estava felizmente programando quando cheguei a um trecho de código que já usei centenas de vezes:

Iterando por meio de uma coleção (aqui ArrayList)

Por algum motivo, eu realmente olhei para as opções de preenchimento automático do Eclipse e fiquei pensando:

Quais casos os seguintes loops são melhores para usar do que os outros?

O clássico loop de índice de matriz:

for (int i = 0; i < collection.length; i++) {
  type array_element = collection.get(index);
}

O Iterator hasNext () / next ():

for (Iterator iterator = collection.iterator(); iterator.hasNext();) {
  type type = (type) iterator.next();   
}

E meu favorito porque é tão simples de escrever:

for (iterable_type iterable_element : collection) {

}
Jason Rogers
fonte
2
Quando se trata de mim, eu uso principalmente o terceiro loop.
Harry Joy,
1
Para a segunda forma, é melhor usar:for (Iterator<type> iterator = collection.iterator(); iterator.hasNext();) { type type = iterator.next(); }
mike jones
4
A interface de coleção não contém um método "get", portanto, o primeiro nem sempre é possível.
ThePerson

Respostas:

104

O primeiro é útil quando você também precisa do índice do elemento. Isso é basicamente equivalente às outras duas variantes de ArrayLists, mas será muito lento se você usar a LinkedList.

O segundo é útil quando você não precisa do índice do elemento, mas pode precisar remover os elementos durante a iteração. Mas isso tem a desvantagem de ser um pouco prolixo demais.

A terceira versão também é minha escolha preferida. É curto e funciona para todos os casos em que você não precisa de nenhum índice ou do iterador subjacente (ou seja, você está apenas acessando elementos, não os removendo ou modificando de Collectionqualquer forma - que é o caso mais comum).

MAK
fonte
3
1 Vença-me. ia mencionar LinkedList (nem todos Collection são baratos para recuperar por índice).
The Scrum Meister
Contanto que seja necessário apenas fazer um loop de elementos, é sempre bom usar a 3ª versão, porque a implementação já existe para todas as diferentes coleções e a Sun ou qualquer implementação JDK se esforça para melhorar o desempenho ao invés de cada uma.
Phani,
@Phani: As outras duas variantes também funcionam em qualquer implementação JDK.
MAK
Eu não disse que isso não funcionaria, mas se por acaso certas melhorias ou alterações de desempenho forem feitas para as implementações de coleções, então isso se aplicaria automaticamente ao seu código e você não precisa escrever para melhorar o desempenho é o que quero dizer.
Phani
1
@Phani: AFAIK a 3ª forma é meramente um açúcar sintático para a 2ª forma (ou seja, sob o capô, a variante foreach realmente usa um iterador). Portanto, quaisquer benefícios de desempenho devem estar disponíveis para ambas as variantes. A primeira versão, é claro, não teria nenhum benefício de desempenho (por exemplo, se o Listfor implementado como uma árvore, na verdade seria mais lento).
MAK
36

Todos eles têm seus próprios usos:

  1. Se você tiver um iterável e precisar atravessar incondicionalmente para todos eles:

    para (iterable_type iterable_element: coleção)

  2. Se você tiver um iterável, mas precisar percorrer condicionalmente:

    for (Iterator iterator = collection.iterator (); iterator.hasNext ();)

  3. Se a estrutura de dados não implementar iterável:

    para (int i = 0; i <comprimento da coleção; i ++)

Suraj Chandran
fonte
Você pode fazer isso para percorrer o primeiro método condicionalmente: if (iterable_element.some_attribute) {// fazer algo; } else {// não faça nada; } Se sim, não há essencialmente nenhuma diferença entre o primeiro e o segundo método, exceto o açúcar sintático.
Thomas Nguyen
1 é tão capaz de atravessar condicionalmente quanto os outros usando breake / oucontinue
arcyqwerty
13

Além disso, há o utilitário stream () das coleções com Java 8

collection.forEach((temp) -> {
            System.out.println(temp);
});

ou

collection.forEach(System.out::println);

Mais informações sobre o Java 8 stream e coleções para link de Wonderers

snr
fonte
4

Nenhum deles é "melhor" do que os outros. O terceiro é, para mim, mais legível, mas para alguém que não usa foreaches pode parecer estranho (eles podem preferir o primeiro). Todos os 3 são bastante claros para qualquer pessoa que entenda de Java, então escolha o que o faz se sentir melhor sobre o código.

O primeiro é o mais básico, portanto, é o padrão mais universal (funciona para arrays, todos os iteráveis ​​que consigo pensar). Essa é a única diferença que consigo pensar. Em casos mais complicados (por exemplo, você precisa ter acesso ao índice atual ou precisa filtrar a lista), o primeiro e o segundo casos podem fazer mais sentido, respectivamente. Para o caso simples (objeto iterável, sem requisitos especiais), o terceiro parece o mais limpo.

Rafe Kettler
fonte
2

A primeira opção é melhor desempenho sábio (como ArrayList implementa interface RandomAccess). De acordo com o documento java, uma implementação de List deve implementar a interface RandomAccess se, para instâncias típicas da classe, este loop:

 for (int i=0, n=list.size(); i < n; i++)
     list.get(i);

corre mais rápido do que este loop:

 for (Iterator i=list.iterator(); i.hasNext(); )
     i.next();

Espero que ajude. A primeira opção seria lenta para listas de acesso sequenciais.

Amitav Padhi
fonte
1

Aqui está um exemplo

Query query = em.createQuery("from Student");
             java.util.List list = query.getResultList();
             for (int i = 0; i < list.size(); i++) 
             {

                 student = (Student) list.get(i);
                 System.out.println(student.id  + "  " + student.age + " " + student.name + " " + student.prenom);

             }
Abdelouhab
fonte