Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to access an iteration-counter in Java's for-each loop?

People also ask

How do I add a counter in forEach loop?

Define an integer outside of the loop, and increment it inside of your loop. Use a for loop instead of foreach, which has a count for you: for(int i = 0; i < array. length; i++) { var item = array[i]; Console.

Can you use forEach on an iterator?

Collections can be iterated easily using two approaches. Using for-Each loop − Use a foreach loop and access the array using object. Using Iterator − Use a foreach loop and access the array using object.


No, but you can provide your own counter.

The reason for this is that the for-each loop internally does not have a counter; it is based on the Iterable interface, i.e. it uses an Iterator to loop through the "collection" - which may not be a collection at all, and may in fact be something not at all based on indexes (such as a linked list).


There is another way.

Given that you write your own Index class and a static method that returns an Iterable over instances of this class you can

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

Where the implementation of With.index is something like

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

The easiest solution is to just run your own counter thus:

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

The reason for this is because there's no actual guarantee that items in a collection (which that variant of for iterates over) even have an index, or even have a defined order (some collections may change the order when you add or remove elements).

See for example, the following code:

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

When you run that, you can see something like:

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

indicating that, rightly so, order is not considered a salient feature of a set.

There are other ways to do it without a manual counter but it's a fair bit of work for dubious benefit.


Using lambdas and functional interfaces in Java 8 makes creating new loop abstractions possible. I can loop over a collection with the index and the collection size:

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

Which outputs:

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

Which I implemented as:

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

The possibilities are endless. For example, I create an abstraction that uses a special function just for the first element:

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

Which prints a comma separated list correctly:

one,two,three,four

Which I implemented as:

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

Libraries will begin to pop up to do these sorts of things, or you can roll your own.