2009-07-08 19 views
122

Necesito ejecutar una lista en orden inverso utilizando Java.¿Se puede hacer una para cada ciclo en Java en orden inverso?

Entonces, ¿dónde esta lo hace hacia delante:

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

¿Hay alguna manera de recorrer la StringList en orden inverso utilizando la sintaxis para cada?

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

+3

Curiosamente, leí esta pregunta a mitad de la lectura de la sección del libro Scala que se ocupa de las operaciones de lista inmutable ... una buena demostración de la falta de fuerza expresiva en Java. – skaffman

+5

El punto del ciclo "para-cada" es que solo necesita realizar una operación en cada elemento, y el orden no es importante.Para-cada uno podría procesar los elementos en orden completamente aleatorio, y todavía estaría haciendo lo que fue diseñado. Si necesita procesar los elementos de una manera particular, le sugiero que lo haga de forma manual. – muusbolla

+0

Biblioteca de colecciones Java. No tiene nada que ver con el lenguaje. Culpa a Josh Bloch. –

Respuesta

129

No utilice el método Collections.reverse que en realidad invierte la lista original en su lugar. Si lo usa:

¡Manera incorrecta!

Collections.reverse(new ArrayList(stringList))

para evitar la modificación de la original, este devuelve una nueva lista con los elementos de la lista original copiado en ella en orden inverso, y tal tiene O (n) los requisitos de rendimiento y el espacio con respecto al tamaño de la lista original.

Como una solución más eficiente, podría escribir una clase que presente una vista invertida de una lista como un genérico Iterable. El iterador devuelto por su clase usaría el ListIterator de la lista decorada para caminar sobre 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 que lo utilizaría como:

import static Reversed.reversed; 

... 

List<String> someStrings = getSomeStrings(); 
for (String s : reversed(someStrings)) { 
    doSomethingWith(s); 
} 
+17

Eso es básicamente lo que hace Iterables.reverse de Google, sí :) –

+9

Sé que hay una 'regla' que tenemos que aceptar la respuesta de Jon :) pero ... Quiero aceptar esta (aunque son 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 ventajas principales de OO - reutilización). –

+0

Pequeño error: en public void remove(), no debería haber una instrucción return, debería ser simplemente: i.remove(); – Jesper

4

AFAIK no hay un tipo de "reverse_iterator" estándar en la biblioteca estándar que admita la sintaxis for-each que ya es un azúcar sintáctico que trajeron tarde al lenguaje.

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

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

+0

Puede ejecutar un ListIterator al revés, que puede completarse dentro de un iterador. –

+0

@Tom: Buen punto. Sin embargo, con el iterador todavía está haciendo el molesto estilo antiguo para bucles, y aún puede pagar el costo para llegar al último elemento para empezar ... Agregué el calificador en mi respuesta, gracias. – Uri

+0

Deque tiene un iterador inverso. –

1

No sin escribir un código personalizado que le dará un enumerador que invertirá los elementos para 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 contenedor (o llame al método, lo que-tiene-usted) que devolvería la implementación Iterable que invierte el elemento en cada bucle.

1

Tendrá que invertir su colección si desea usar la sintaxis para cada sintaxis y proceder en orden inverso.

84

Para obtener una lista, usted podría utilizar el Google Guava Library:

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

Tenga en cuenta que Lists.reverse no significa revertir toda la colección, o hacer cualquier cosa como él - sólo se permite la iteración y de acceso aleatorio, en el orden inverso. Esto es más eficiente que revertir la colección primero.

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

(si no está ya usarlo, me fondo recomendamos echar un vistazo a la Guava. Es una gran cosa.)

+1

Nuestra base de código hace un uso intensivo de la versión genérica de Commons Collections lanzada por larvalabs (http://larvalabs.com/collections/). Mirando a través del repositorio de SVN para Apache Commons, está claro que la mayor parte del trabajo de lanzamiento de una versión java 5 de Commons Collections está hecho, simplemente no lo han lanzado aún. – skaffman

+0

Me gusta. Si no fuera tan útil, lo llamaría un enchufe. – geowa4

+0

Me pregunto por qué Jakarta nunca se molestó en actualizar Apache Commons. – Uri

8

Esto se hace un lío con la lista original y también tiene que ser llamado fuera del bucle. Además, no desea realizar un retroceso cada vez que realice un ciclo; ¿sería eso cierto si se aplicara uno de los Iterables.reverse ideas?

Collections.reverse(stringList); 

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

Esto puede ser una opción. Espero que haya una mejor manera de comenzar desde el último elemento que desde el ciclo 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);   
    } 
} 
27

La lista (a diferencia del conjunto) es una colección ordenada e iterar sobre ella conserva el pedido por contrato. Hubiera esperado que una Pila iterara en el orden inverso, pero desafortunadamente no. Por lo que la solución más simple que puedo pensar es la siguiente:

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

Soy consciente de que esto no es un "para cada" bucle de solución. Prefiero utilizar el bucle for que introducir 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.

+2

Este enfoque puede estar bien para las listas basadas en arreglos (como ArrayList) pero sería subóptimo para las listas vinculadas ya que cada obtención tendría que atravesar la lista de principio a fin (o posiblemente de principio a fin) para cada obtención. Es mejor utilizar un iterador más inteligente que 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 sintaxis 'for each' –

1

A partir de the comment: Usted debe ser capaz de utilizar Apache Commons ReverseListIterator

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

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

Como @rogerdpack said, es necesario envolver la ReverseListIterator como Iterable.

+1

Desafortunadamente que devuelve un" Iterador "en lugar de un" Iterable "por lo que no puede, parece ser usado en un bucle foreach: | – rogerdpack

+0

@rogerdpack: Gracias. Vea la actualización para la solución alternativa. –

0

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

Aquí está la solución copiado del Pensando en Java Edición cuarto, 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 
*///:~ 
+0

¿por qué el 'int current = size() - 1' correcto? ¿por qué no el 'int current = this.size() - 1' o' int current = super.size() - 1' –

Cuestiones relacionadas