Is there a way to access an iteration-counter in J

2019-01-02 17:55发布

Is there a way in Java's for-each loop

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

to find out how often the loop has already been processed?

Aside from using the old and well-known for(int i=0; i < boundary; i++) - loop, is the construct

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

the only way to have such a counter available in a for-each loop?

14条回答
ら面具成の殇う
2楼-- · 2019-01-02 18:26

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.

查看更多
只靠听说
3楼-- · 2019-01-02 18:31

There is a "variant" to pax' answer... ;-)

int i = -1;
for(String s : stringArray) {
    doSomethingWith(s, ++i);
}
查看更多
浮光初槿花落
4楼-- · 2019-01-02 18:33

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.

查看更多
泛滥B
5楼-- · 2019-01-02 18:41

If you need a counter in an for-each loop you have to count yourself. There is no built in counter as far as I know.

查看更多
浮光初槿花落
6楼-- · 2019-01-02 18:42

For situations where I only need the index occasionally, like in a catch clause, I will sometimes use 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);
  }
}
查看更多
只靠听说
7楼-- · 2019-01-02 18:47

I found that using a simple for loop with an incrementing index was the most efficient, reliable solution, as posted here: https://stackoverflow.com/a/3431543/2430549

查看更多
登录 后发表回答