¿Hay alguna manera de acceder a un contador de iteraciones en el bucle for-each de Java?

¿Hay alguna forma en el bucle for-each de Java?

for(String s : stringArray) { doSomethingWith(s); } 

para averiguar con qué frecuencia el bucle ya se ha procesado?

Además de usar el viejo y conocido for(int i=0; i < boundary; i++) – loop, es la construcción

 int i = 0; for(String s : stringArray) { doSomethingWith(s); i++; } 

la única forma de tener un contador disponible en un bucle for-each?

No, pero puedes proporcionar tu propio contador.

La razón para esto es que el bucle for-each internamente no tiene contador; se basa en la interfaz Iterable , es decir, utiliza un Iterator para recorrer la “colección”, que puede no ser una colección, y de hecho puede ser algo en absoluto basado en índices (como una lista vinculada).

Hay otra manera.

Dado que usted escribe su propia clase Index y un método estático que devuelve un Iterable sobre las instancias de esta clase, puede

 for (Index each: With.index(stringArray)) { each.value; each.index; ... } 

Donde la implementación de With.index es algo así como

 class With { public static  Iterable> index(final T[] array) { return new Iterable>() { public Iterator> iterator() { return new Iterator>() { index = 0; public boolean hasNext() { return index < array.size } public Index next() { return new Index(array[index], index++); } ... } } } } } 

La solución más fácil es simplemente ejecutar su propio contador de esta manera:

 int i = 0; for (String s : stringArray) { doSomethingWith(s, i); i++; } 

La razón de esto es porque no hay garantía real de que los elementos de una colección (cuya variante for iterar) incluso tengan un índice, o incluso tengan un orden definido (algunas colecciones pueden cambiar el orden cuando agrega o elimina elementos).

Ver por ejemplo, el siguiente código:

 import java.util.*; public class TestApp { public static void AddAndDump(AbstractSet set, String str) { System.out.println("Adding [" + str + "]"); set.add(str); int i = 0; for(String s : set) { System.out.println(" " + i + ": " + s); i++; } } public static void main(String[] args) { AbstractSet coll = new HashSet(); AddAndDump(coll, "Hello"); AddAndDump(coll, "My"); AddAndDump(coll, "Name"); AddAndDump(coll, "Is"); AddAndDump(coll, "Pax"); } } 

Cuando ejecuta eso, puede ver algo como:

 Adding [Hello] 0: Hello Adding [My] 0: Hello 1: My Adding [Name] 0: Hello 1: My 2: Name Adding [Is] 0: Hello 1: Is 2: My 3: Name Adding [Pax] 0: Hello 1: Pax 2: Is 3: My 4: Name 

indicando que, con razón, el orden no se considera una característica sobresaliente de un conjunto.

Hay otras maneras de hacerlo sin un contador manual, pero es un poco de trabajo para beneficio dudoso.

El uso de lambdas y las interfaces funcionales en Java 8 hace posible la creación de nuevas abstracciones de bucle. Puedo recorrer una colección con el índice y el tamaño de la colección:

 List strings = Arrays.asList("one", "two","three","four"); forEach(strings, (x, i, n) -> System.out.println("" + (i+1) + "/"+n+": " + x)); 

Qué salidas:

 1/4: one 2/4: two 3/4: three 4/4: four 

Que implementé como:

  @FunctionalInterface public interface LoopWithIndexAndSizeConsumer { void accept(T t, int i, int n); } public static  void forEach(Collection collection, LoopWithIndexAndSizeConsumer consumer) { int index = 0; for (T object : collection){ consumer.accept(object, index++, collection.size()); } } 

Las posibilidades son infinitas. Por ejemplo, creo una abstracción que usa una función especial solo para el primer elemento:

 forEachHeadTail(strings, (head) -> System.out.print(head), (tail) -> System.out.print(","+tail)); 

Que imprime una lista separada por comas correctamente:

 one,two,three,four 

Que implementé como:

 public static  void forEachHeadTail(Collection collection, Consumer headFunc, Consumer tailFunc) { int index = 0; for (T object : collection){ if (index++ == 0){ headFunc.accept(object); } else{ tailFunc.accept(object); } } } 

Las bibliotecas comenzarán a aparecer para hacer este tipo de cosas, o puede hacer las suyas propias.

Me temo que esto no es posible con foreach . Pero puedo sugerirle un simple for-old estilo bucles :

  List l = new ArrayList(); l.add("a"); l.add("b"); l.add("c"); l.add("d"); // the array String[] array = new String[l.size()]; for(ListIterator it =l.listIterator(); it.hasNext() ;) { array[it.nextIndex()] = it.next(); } 

Tenga en cuenta que la interfaz de la Lista le da acceso a it.nextIndex() .

(editar)

Para su ejemplo cambiado:

  for(ListIterator it =l.listIterator(); it.hasNext() ;) { int i = it.nextIndex(); doSomethingWith(it.next(), i); } 

Java 8 introdujo el Iterable#forEach() / Map#forEach() , que es más eficiente para muchas implementaciones de Collection / Map comparación con el bucle for-each “clásico”. Sin embargo, también en este caso, no se proporciona un índice. El truco aquí es usar AtomicInteger fuera de la expresión lambda. Nota: las variables utilizadas en la expresión lambda deben ser efectivamente finales, es por eso que no podemos usar una int normal.

 final AtomicInteger indexHolder = new AtomicInteger(); map.forEach((k, v) -> { final int index = indexHolder.getAndIncrement(); // use the index }); 

Uno de los cambios que Sun está considerando para Java7 es proporcionar acceso al Iterator interno en los bucles foreach. la syntax será algo como esto (si esto es aceptado):

 for (String str : list : it) { if (str.length() > 100) { it.remove(); } } 

Este es el azúcar sintáctico, pero aparentemente se hicieron muchas solicitudes para esta característica. Pero hasta que se apruebe, tendrá que contar las iteraciones usted mismo, o usar un bucle for para un Iterator .

Solución idiomática:

 final Set doubles; // boilerplate final Iterator iterator = doubles.iterator(); for (int ordinal = 0; iterator.hasNext(); ordinal++) { System.out.printf("%d:%f",ordinal,iterator.next()); System.out.println(); } 

esta es en realidad la solución que Google sugiere en el debate de Guava sobre por qué no proporcionaron un CountingIterator .

Si necesita un contador en un bucle for-each, debe contar usted mismo. No hay un mostrador incorporado por lo que sé.

Hay una respuesta de “variante” a pax … 😉

 int i = -1; for(String s : stringArray) { doSomethingWith(s, ++i); } 
 int i = 0; for(String s : stringArray) { doSomethingWith(s,i); ++i; } 

Hay muchas maneras, pero el método es uno de los

Descubrí que el uso de un bucle simple con un índice creciente era la solución más eficiente y confiable, como se publica aquí: https://stackoverflow.com/a/3431543/2430549

Para situaciones en las que solo necesito el índice ocasionalmente, como en una cláusula catch, a veces uso indexOf.

 for(String s : stringArray) { try { doSomethingWith(s); } catch (Exception e) { LOGGER.warn("Had some kind of problem with string " + stringArray.indexOf(s) + ": " + s, e); } } 

Estoy un poco sorprendido de que nadie haya sugerido lo siguiente (admito que es un enfoque perezoso …); Si stringArray es una Lista de algún tipo, puede usar algo como stringArray.indexOf (S) para devolver un valor para el recuento actual.

Nota: esto supone que los elementos de la Lista son únicos, o que no importa si no son únicos (porque en ese caso devolverá el índice de la primera copia encontrada).

Hay situaciones en las que eso sería suficiente …