Pode-se fazer um para cada loop em java na ordem inversa?

148

Preciso executar uma lista na ordem inversa usando Java.

Então, onde isso encaminha:

for(String string: stringList){
//...do something
}

Existe alguma maneira de iterar o stringList na ordem inversa usando o para cada sintaxe?

Para maior clareza: sei como iterar uma lista em ordem inversa, mas gostaria de saber (por curiosidade) como fazê-lo para cada estilo.

Ron Tuffin
fonte
5
O objetivo do loop "for-each" é que você só precisa executar uma operação em cada elemento, e a ordem não é importante. Pois cada um poderia processar os elementos em ordem completamente aleatória, e ainda estaria fazendo o que foi projetado. Se você precisar processar os elementos de uma maneira específica, sugiro fazê-lo manualmente.
8339 muusbolla
Biblioteca de coleções Java. Não tem nada a ver com o idioma. Culpe Josh Bloch.
Tom Hawtin # tackline 08/07/09
7
@muusbolla: Mas como uma lista é uma coleção ordenada , certamente seu pedido será respeitado, independentemente? Portanto, for-each não processará os elementos de uma lista em uma ordem aleatória.
Lee Kowalkowski 22/03
5
@muusbolla isso não é verdade. Talvez no caso de Setcoleções derivadas. foreachgarante a iteração na ordem do iterador retornado do iterator()método da coleção. docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

Respostas:

151

O método Collections.reverse, na verdade, retorna uma nova lista com os elementos da lista original copiados nela na ordem inversa, portanto, esse desempenho tem O (n) em relação ao tamanho da lista original.

Como uma solução mais eficiente, você pode escrever um decorador que apresente uma visão invertida de uma lista como iterável. O iterador retornado pelo decorador usaria o ListIterator da lista decorada para percorrer os elementos na ordem inversa.

Por exemplo:

public class Reversed<T> implements Iterable<T> {
    private final List<T> original;

    public Reversed(List<T> original) {
        this.original = original;
    }

    public Iterator<T> iterator() {
        final ListIterator<T> i = original.listIterator(original.size());

        return new Iterator<T>() {
            public boolean hasNext() { return i.hasPrevious(); }
            public T next() { return i.previous(); }
            public void remove() { i.remove(); }
        };
    }

    public static <T> Reversed<T> reversed(List<T> original) {
        return new Reversed<T>(original);
    }
}

E você usaria como:

import static Reversed.reversed;

...

List<String> someStrings = getSomeStrings();
for (String s : reversed(someStrings)) {
    doSomethingWith(s);
}
Nat
fonte
22
Isso é basicamente o que Iterables.reverse do Google faz, sim :)
Jon Skeet
10
Eu sei que existe uma 'regra' de que temos que aceitar a resposta de Jon :) mas .. Eu quero aceitar essa (mesmo que sejam essencialmente iguais) porque não exige que eu inclua outra biblioteca de terceiros (embora alguns podem argumentar que essa razão quebra uma das principais vantagens da OO - reutilização).
22413 Ron Tuffin
Pequeno erro: em public void remove (), não deve haver uma declaração de retorno, deve ser apenas: i.remove ();
Jesper
10
Collections.reverse () NÃO retorna uma cópia invertida, mas atua na lista passada a ela como um parâmetro. Como sua solução com o iterador, no entanto. Realmente elegante.
Er4z0r
96

Para uma lista, você pode usar a Biblioteca do Google Guava :

for (String item : Lists.reverse(stringList))
{
    // ...
}

Observe que não inverte toda a coleção ou faz algo parecido - apenas permite iteração e acesso aleatório, na ordem inversa. Isso é mais eficiente do que reverter a coleção primeiro.Lists.reverse

Para reverter uma iteração arbitrária, você teria que ler tudo e depois "reproduzir" de trás para frente.

(Se você não estiver usando-lo, eu completamente recomendo que você tem um olhar para o Goiaba . É uma grande coisa.)

Jon Skeet
fonte
1
Nossa base de código faz uso pesado da versão gerada do Commons Collections lançada pela larvalabs ( larvalabs.com/collections ). Examinando o repositório SVN do Apache Commons, fica claro que a maior parte do trabalho de lançamento de uma versão java 5 do Commons Collections está concluída, mas eles ainda não o lançaram.
21413 skaffman
Eu gosto disso. Se não fosse tão útil, eu chamaria isso de plugue.
geowa4
Eu me pergunto por que Jacarta nunca se incomodou em atualizar o Apache Commons.
Uri
3
Eles o atualizaram, é o que estou dizendo. Eles simplesmente não o lançaram.
28410 skaffman
23
Iterables.reverse foi descontinuado, use Lists.reverse ou ImmutableList.reverse.
Garrett Hall
39

A Lista (diferente do Conjunto) é uma coleção ordenada e a iteração sobre ela preserva a ordem por contrato. Eu esperaria que um Stack iterasse na ordem inversa, mas infelizmente não. Portanto, a solução mais simples que consigo pensar é a seguinte:

for (int i = stack.size() - 1; i >= 0; i--) {
    System.out.println(stack.get(i));
}

Percebo que essa não é uma solução de loop "para cada". Prefiro usar o loop for do que introduzir uma nova biblioteca como as coleções do Google.

Collections.reverse () também faz o trabalho, mas atualiza a lista em vez de retornar uma cópia na ordem inversa.

Gopi Reddy
fonte
3
Essa abordagem pode ser adequada para listas baseadas em matriz (como ArrayList), mas seria sub-ideal para Listas Vinculadas, pois cada obtenção precisa percorrer a lista do começo ao fim (ou possivelmente do início ao fim) para cada obtenção. É melhor usar um iterador mais inteligente como na solução de Nat (ideal para todas as implementações da List).
Chris
1
Além disso, ele se afasta da solicitação no OP, que solicita explicitamente a for eachsintaxe
Paul W
8

Isso mexerá com a lista original e também precisará ser chamado fora do loop. Além disso, você não deseja executar um reverso toda vez que fizer um loop - isso seria verdade se um deles Iterables.reverse ideasfosse aplicado?

Collections.reverse(stringList);

for(String string: stringList){
//...do something
}
Phillip Gibb
fonte
5

AFAIK, não existe um tipo padrão de "reverse_iterator" na biblioteca padrão que suporte a sintaxe de cada um, que já é um açúcar sintático que eles trouxeram tarde para o idioma.

Você pode fazer algo como (elemento do item: myList.clone (). Reverse ()) e pagar o preço associado.

Isso também parece bastante consistente com o aparente fenômeno de não fornecer maneiras convenientes de executar operações caras - uma vez que uma lista, por definição, pode ter complexidade de acesso aleatório O (N) (você pode implementar a interface com um link único), reverter a iteração pode acabar sendo O (N ^ 2). Obviamente, se você possui um ArrayList, não paga esse preço.

Uri
fonte
Você pode executar um ListIterator para trás, que pode ser agrupado em um Iterator.
Tom Hawtin # tackline
@ Tom: Bom ponto. No entanto, com o iterador, você ainda está fazendo o estilo antigo irritante para loops, e ainda pode pagar o custo para chegar ao último elemento para começar ... No entanto, adicionei o qualificador na minha resposta, obrigado.
Uri
O Deque possui um iterador reverso.
27510 Michael Munsey
2

Isso pode ser uma opção. Espero que exista uma maneira melhor de começar do último elemento do que para o loop while até o fim.

public static void main(String[] args) {        
    List<String> a = new ArrayList<String>();
    a.add("1");a.add("2");a.add("3");a.add("4");a.add("5");

    ListIterator<String> aIter=a.listIterator();        
    while(aIter.hasNext()) aIter.next();

    for (;aIter.hasPrevious();)
    {
        String aVal = aIter.previous();
        System.out.println(aVal);           
    }
}
Krishna
fonte
1

Não sem escrever um código personalizado que fornecerá um enumerador que reverterá os elementos para você.

Você deve conseguir fazer isso em Java, criando uma implementação customizada do Iterable que retornará os elementos na ordem inversa.

Em seguida, você instanciaria o wrapper (ou chamaria o método what-have-you) que retornaria a implementação Iterable que inverte o elemento no loop de cada loop.

casperOne
fonte
1

Você precisaria reverter sua coleção se quiser usar a sintaxe para cada caixa pronta e seguir na ordem inversa.

Owen
fonte
1

Todas as respostas acima cumprem apenas o requisito, envolvendo outro método ou chamando algum código estrangeiro para fora;

Aqui está a solução copiado do Thinking in Java 4ª edição , capítulo 11.13.1 AdapterMethodIdiom ;

Aqui está o código:

// The "Adapter Method" idiom allows you to use foreach
// with additional kinds of Iterables.
package holding;
import java.util.*;

@SuppressWarnings("serial")
class ReversibleArrayList<T> extends ArrayList<T> {
  public ReversibleArrayList(Collection<T> c) { super(c); }
  public Iterable<T> reversed() {
    return new Iterable<T>() {
      public Iterator<T> iterator() {
        return new Iterator<T>() {
          int current = size() - 1; //why this.size() or super.size() wrong?
          public boolean hasNext() { return current > -1; }
          public T next() { return get(current--); }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }
}   

public class AdapterMethodIdiom {
  public static void main(String[] args) {
    ReversibleArrayList<String> ral =
      new ReversibleArrayList<String>(
        Arrays.asList("To be or not to be".split(" ")));
    // Grabs the ordinary iterator via iterator():
    for(String s : ral)
      System.out.print(s + " ");
    System.out.println();
    // Hand it the Iterable of your choice
    for(String s : ral.reversed())
      System.out.print(s + " ");
  }
} /* Output:
To be or not to be
be to not or be To
*///:~
qiwen li
fonte
por que está int current = size() - 1certo? por que não o int current = this.size() - 1ouint current = super.size() - 1
qiwen li
1

Um trabalho em torno de:

Collections.reverse(stringList).forEach(str -> ...);

Ou com goiaba :

Lists.reverse(stringList).forEach(str -> ...);
EssaidiM
fonte
0

Definitivamente, uma resposta tardia a esta pergunta. Uma possibilidade é usar o ListIterator em um loop for. Não é tão limpo quanto a sintaxe dos dois pontos, mas funciona.

List<String> exampleList = new ArrayList<>();
exampleList.add("One");
exampleList.add("Two");
exampleList.add("Three");

//Forward iteration
for (String currentString : exampleList) {
    System.out.println(currentString); 
}

//Reverse iteration
for (ListIterator<String> itr = exampleList.listIterator(exampleList.size()); itr.hasPrevious(); /*no-op*/ ) {
    String currentString = itr.previous();
    System.out.println(currentString); 
}

O crédito para a sintaxe do ListIterator vai para "Maneiras de iterar sobre uma lista em Java"

ScottMichaud
fonte