Y a-t-il un moyen d'accéder à un compteur d'itérations dans Java pour chaque boucle?

Est-il possible en Java de la boucle for-each

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

pour savoir combien de fois la boucle a déjà été traité?

mis à part l'utilisation de l'ancien et bien connu for(int i=0; i < boundary; i++) - boucle, est la construction

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

la seule façon d'avoir un compteur disponible dans une boucle for-each?

228
demandé sur trapzerapzerix 2009-01-25 14:05:37

14 réponses

Non, mais vous pouvez fournir votre propre compteur.

la raison en est que la boucle pour-chaque en interne ne a un compteur; il est basé sur le itérable interface, c. - à-d. il utilise un Iterator pour boucle à travers la" collection " - qui peut ne pas être une collection du tout, et peut en fait être quelque chose pas du tout basé sur des index (comme une liste liée).

167
répondu Michael Borgwardt 2016-09-29 20:49:18

il y a un autre moyen.

étant donné que vous écrivez votre propre classe Index et une méthode statique qui retourne un Iterable sur les instances de cette classe vous pouvez

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

où la mise en œuvre de With.index est quelque chose comme

class With {
    public static <T> Iterable<Index<T>> index(final T[] array) {
        return new Iterable<Index<T>>() {
            public Iterator<Index<T>> iterator() {
                return new Iterator<Index<T>>() {
                    index = 0;
                    public boolean hasNext() { return index < array.size }
                    public Index<T> next() { return new Index(array[index], index++); }
                    ...
                }
            }
        }
    }
}
56
répondu akuhn 2009-09-29 08:45:30

la solution la plus facile est pour juste exécuter votre propre compteur ainsi:

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

la raison en est qu'il n'y a pas de garantie réelle que les articles dans une collection (que cette variante de for itère plus) même ont un index, ou même avoir un ordre défini (certaines collections peuvent changer l'ordre quand vous ajoutez ou supprimez des éléments).

voir par exemple, le code suivant:

import java.util.*;

public class TestApp {
  public static void AddAndDump(AbstractSet<String> 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<String> coll = new HashSet<String>();
    AddAndDump(coll, "Hello");
    AddAndDump(coll, "My");
    AddAndDump(coll, "Name");
    AddAndDump(coll, "Is");
    AddAndDump(coll, "Pax");
  }
}

quand vous exécutez cela, vous pouvez voir quelque chose comme:

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

, indiquant que, à juste titre, l'ordre n'est pas considéré comme une caractéristique de l'ensemble.

il y a d'autres façons de le faire sans un compteur manuel, mais c'est un peu de travail pour un bénéfice douteux.

36
répondu paxdiablo 2016-12-22 06:53:57

utilisant lambdas et interfaces fonctionnelles dans Java 8 rend la création de nouvelles abstractions de boucle possible. Je peux boucler une collection avec l'index et la taille de la collection:

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

qui produit:

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

que j'ai implémenté comme:

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

Les possibilités sont infinies. Par exemple, je crée une abstraction qui utilise une fonction spéciale juste pour le premier élément:

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

qui imprime correctement une liste séparée par des virgules:

one,two,three,four

que j'ai implémenté comme:

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

les bibliothèques commenceront à apparaître pour faire ce genre de choses, ou vous pouvez rouler votre propre.

22
répondu James Scriven 2014-04-05 13:22:47

je crains que ce ne soit pas possible avec foreach . Mais je peux vous suggérer un simple vieux-style pour-boucles :

    List<String> l = new ArrayList<String>();

    l.add("a");
    l.add("b");
    l.add("c");
    l.add("d");

    // the array
    String[] array = new String[l.size()];

    for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
    {
        array[it.nextIndex()] = it.next();
    }

notez que, la liste interface vous donne accès à it.nextIndex() .

(edit)

à votre exemple modifié:

    for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
    {
        int i = it.nextIndex();
        doSomethingWith(it.next(), i);
    }
16
répondu bruno conde 2009-01-25 11:54:13

Java 8 a introduit la méthode Iterable#forEach() / Map#forEach() , qui est plus efficace pour de nombreuses implémentations Collection / Map par rapport à la méthode" classique " pour chaque boucle. Toutefois, dans ce cas également un index n'est pas fourni. Le truc ici est d'utiliser AtomicInteger en dehors de l'expression lambda. Note: les variables utilisées dans l'expression lambda doivent être effectivement finales, c'est pourquoi nous ne pouvons pas utiliser un int ordinaire .

final AtomicInteger indexHolder = new AtomicInteger();
map.forEach((k, v) -> {
    final int index = indexHolder.getAndIncrement();
    // use the index
});
10
répondu rmuller 2016-10-19 16:52:16

un des changements Sun est considéré pour Java7 est de fournir l'accès à l'intérieur Iterator dans les boucles de foreach. la syntaxe sera quelque chose comme ceci (si c'est accepté):

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

C'est sucre syntaxique, mais apparemment beaucoup de demandes ont été faites pour cette fonction. Mais jusqu'à ce qu'il soit approuvé, vous devrez compter les itérations vous-même, ou utiliser un régulier pour boucle avec un Iterator .

9
répondu Yuval 2017-12-29 02:04:07

Solution Idiomatique:

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

C'est en fait la solution que Google suggère dans la discussion sur la goyave sur la raison pour laquelle ils n'ont pas fourni un CountingIterator .

5
répondu feeling abused and harassed 2017-10-13 03:25:32

si vous avez besoin d'un compteur dans une boucle pour chaque boucle, vous devez vous compter. Il n'y a pas de comptoir construit à ma connaissance.

2
répondu EricSchaefer 2009-09-29 08:58:39

il y a une" variante " à la réponse de pax... ;- )

int i = -1;
for(String s : stringArray) {
    doSomethingWith(s, ++i);
}
1
répondu Jörg 2010-06-30 13:13:29
int i = 0;
for(String s : stringArray) {
    doSomethingWith(s,i);
    ++i;
} 

il y a plusieurs façons mais la méthode est l'une des façons

1
répondu Sankar Karthi 2016-06-14 09:54:43

j'ai trouvé que l'utilisation d'un simple pour boucle avec un indice d'incrémentation était la solution la plus efficace et fiable, comme posté ici: https://stackoverflow.com/a/3431543/2430549

0
répondu HoldOffHunger 2017-06-02 00:05:47

pour les situations où je n'ai besoin de l'index qu'occasionnellement, comme dans une clause de catch, j'utiliserai parfois 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);
  }
}
0
répondu Jeremy 2017-06-14 17:23:05

je suis un peu surpris que personne n'ait suggéré ce qui suit (je reconnais que c'est une approche paresseuse...); Si stringArray est une liste, vous pourriez utiliser quelque chose comme stringArray.indexOf (S) pour retourner une valeur pour le compte courant.

Note: ceci suppose que les éléments de la liste sont uniques, ou qu'il n'importe pas s'ils ne sont pas uniques (parce que dans ce cas, il retournera l'index de la première copie trouvée).

il y a des situations qui qui serait suffisant...

-4
répondu Rik 2011-03-04 23:02:27