¿Se puede hacer un para cada bucle en Java en orden inverso?

148

Necesito ejecutar una lista en orden inverso usando Java.

Entonces, ¿dónde lo hace?

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

¿Hay alguna forma de iterar el stringList en orden inverso usando el para cada sintaxis?

Para mayor claridad: sé cómo iterar una lista en orden inverso, pero me gustaría saber (por curiosidad) cómo hacerlo en cada estilo.

Ron Tuffin
fuente
55
El punto del ciclo "para cada uno" es que solo necesita realizar una operación en cada elemento, y el orden no es importante. For-each podría procesar los elementos en un orden completamente aleatorio, y aún estaría haciendo para lo que fue diseñado. Si necesita procesar los elementos de una manera particular, le sugiero que lo haga manualmente.
muusbolla
Biblioteca de colecciones de Java. Realmente no tiene nada que ver con el idioma. Culpa a Josh Bloch.
Tom Hawtin - tackline el
77
@muusbolla: Pero como una Lista es una colección ordenada , ¿seguramente se respetará su orden? Por lo tanto, for-each no procesará los elementos de una Lista en un orden aleatorio.
Lee Kowalkowski
55
@muusbolla eso no es cierto. Quizás en el caso de Setcolecciones derivadas. foreachgarantiza la iteración en el orden del iterador devuelto por el iterator()método de la colección. docs.oracle.com/javase/1.5.0/docs/guide/language/foreach.html
robert

Respuestas:

151

El método Collections.reverse en realidad devuelve una nueva lista con los elementos de la lista original copiados en orden inverso, por lo que tiene un rendimiento O (n) con respecto al tamaño de la lista original.

Como una solución más eficiente, podría escribir un decorador que presente una vista inversa de una Lista como Iterable. El iterador devuelto por su decorador usaría el ListIterator de la lista decorada para recorrer los elementos en orden inverso.

Por ejemplo:

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);
    }
}

Y lo usarías como:

import static Reversed.reversed;

...

List<String> someStrings = getSomeStrings();
for (String s : reversed(someStrings)) {
    doSomethingWith(s);
}
Nat
fuente
22
Eso es básicamente lo que hace Iterables.reverse de Google, sí :)
Jon Skeet
10
Sé que hay una 'regla' de que tenemos que aceptar la respuesta de Jon :) pero ... Quiero aceptar esta (aunque sean esencialmente las mismas) porque no requiere que incluya otra biblioteca de terceros (aunque algunos podrían argumentar que esa razón rompe una de las principales ventajas de OO: la reutilización
Ron Tuffin
Pequeño error: en public void remove (), no debería haber una declaración de devolución, debería ser solo: i.remove ();
Jesper
10
Collections.reverse () NO devuelve una copia invertida, sino que actúa en la Lista que se le pasa como parámetro. Sin embargo, me gusta su solución con el iterador. Muy elegante
er4z0r
96

Para obtener una lista, puede utilizar la Biblioteca de guayaba de Google :

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

Tenga en cuenta que no invierte toda la colección, ni hace nada parecido, solo permite la iteración y el acceso aleatorio, en el orden inverso. Esto es más eficiente que invertir la colección primero.Lists.reverse

Para revertir un iterativo arbitrario, tendría que leerlo todo y luego "reproducirlo" al revés.

(Si no está ya a usarlo, me gustaría a fondo recomendamos echar un vistazo a la guayaba . Es una gran cosa.)

Jon Skeet
fuente
1
Nuestra base de código hace un uso intensivo de la versión genérica de Commons Collections lanzada por larvalabs ( larvalabs.com/collections ). Mirando a través del repositorio de SVN para Apache Commons, está claro que la mayor parte del trabajo en el lanzamiento de una versión java 5 de Commons Collections está hecho, simplemente no lo han lanzado todavía.
skaffman
Me gusta. Si no fuera tan útil, lo llamaría un enchufe.
geowa4
Me pregunto por qué Yakarta nunca se molestó en actualizar Apache Commons.
Uri
3
Lo han actualizado, eso es lo que digo. Simplemente no lo han lanzado.
skaffman
23
Iterables.reverse ha quedado en desuso, use Lists.reverse o ImmutableList.reverse en su lugar.
Garrett Hall
39

La Lista (a diferencia del Conjunto) es una colección ordenada e iterar sobre ella conserva el orden por contrato. Hubiera esperado que una pila iterara en el orden inverso, pero desafortunadamente no lo hace. Entonces, la solución más simple que se me ocurre es esta:

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

Me doy cuenta de que esta no es una solución de bucle "para cada". Prefiero usar el bucle for que presentar una nueva biblioteca como Google Collections.

Collections.reverse () también hace el trabajo pero actualiza la lista en lugar de devolver una copia en orden inverso.

Gopi Reddy
fuente
3
Este enfoque puede ser adecuado para listas basadas en matrices (como ArrayList), pero sería subóptimo para las Listas vinculadas, ya que cada get tendría que recorrer la lista de principio a fin (o posiblemente de principio a fin) para cada get. Es mejor usar un iterador más inteligente como en la solución de Nat (óptimo para todas las implementaciones de List).
Chris
1
Además, se desvía de la solicitud en el OP que solicita explícitamente la for eachsintaxis
Paul W
8

Esto alterará la lista original y también debe llamarse fuera del bucle. Además, no desea realizar una inversión cada vez que realiza un bucle, ¿sería eso cierto si Iterables.reverse ideasse aplicara uno de los ?

Collections.reverse(stringList);

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

AFAIK no hay un tipo estándar de "reverse_iterator" en la biblioteca estándar que admita la sintaxis para cada uno, que ya es un azúcar sintáctico que introdujeron tarde en el lenguaje.

Puede hacer algo como por (Elemento del elemento: myList.clone (). Reverse ()) y pagar el precio asociado.

Esto también parece bastante coherente con el fenómeno aparente de no brindarle formas convenientes de realizar operaciones costosas, ya que una lista, por definición, podría tener una complejidad de acceso aleatorio O (N) (podría implementar la interfaz con un solo enlace), revertir la iteración podría terminar siendo O (N ^ 2). Por supuesto, si tiene una ArrayList, no paga ese precio.

Uri
fuente
Puede ejecutar un ListIterator al revés, que puede envolverse dentro de un Iterator.
Tom Hawtin - tackline el
@Tom: Buen punto. Sin embargo, con el iterador todavía está haciendo el molesto estilo antiguo para los bucles, y aún puede pagar el costo para llegar al último elemento para comenzar ... Sin embargo, agregué el calificador en mi respuesta, gracias.
Uri
Deque tiene un iterador inverso.
Michael Munsey
2

Esto puede ser una opción. Espero que haya una mejor manera de comenzar desde el último elemento que hasta el bucle while hasta el final.

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
fuente
2

A partir del comentario : Debería poder usar Apache CommonsReverseListIterator

Iterable<String> reverse 
    = new IteratorIterable(new ReverseListIterator(stringList));

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

Como dijo @rogerdpack , debe envolver el ReverseListIteratorcomo Iterable.

serv-inc
fuente
1

No sin escribir un código personalizado que le dará un enumerador que revertirá los elementos por usted.

Debería poder hacerlo en Java creando una implementación personalizada de Iterable que devolverá los elementos en orden inverso.

Luego, creará una instancia del reiniciador (o llamará al método what-have-you) que devolvería la implementación Iterable que invierte el elemento en cada ciclo.

casperOne
fuente
1

Tendrá que revertir su colección si desea usar la sintaxis para cada caja y hacerla en orden inverso.

Owen
fuente
1

Todas las respuestas anteriores solo cumplen el requisito, ya sea envolviendo otro método o llamando a algún código extranjero fuera;

Aquí está la solución copiada de Thinking in Java 4th edition , capítulo 11.13.1 AdapterMethodIdiom ;

Aquí está el 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
fuente
¿Por qué es lo int current = size() - 1correcto? ¿por qué no el int current = this.size() - 1oint current = super.size() - 1
li Qiwen
1

Un trabajo alrededor:

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

O con guayaba :

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

Definitivamente una respuesta tardía a esta pregunta. Una posibilidad es usar el ListIterator en un bucle for. No es tan limpio como la sintaxis de dos puntos, pero 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); 
}

El crédito por la sintaxis de ListIterator va a "Formas de iterar sobre una lista en Java"

ScottMichaud
fuente