有没有办法在Java的for-each循环中访问迭代计数器?

有没有在Java的每个循环的方式

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

找出循环已经被处理了多久?

除了使用旧的和已知for(int i=0; i < boundary; i++) – 循环,是构造

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

唯一的方法是在每个循环中都有这样一个计数器?

不,但你可以提供你自己的计数器。

原因是for-each循环内部没有计数器; 它基于Iterable接口,即它使用Iterator来循环“集合” – 可能根本不是集合,实际上可能根本不是基于索引(如链接列表)的东西。

还有另一种方法。

鉴于您编写自己的Index类和一个静态方法,可以通过这个类的实例返回一个Iterable

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

With.index的实现类似于

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

最简单的解决scheme就是运行自己的计数器:

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

原因是因为没有实际的保证集合中的项目(即迭代的变体)甚至索引,甚至没有定义的顺序 (当添加或删除元素时,某些集合可能会改变顺序)。

例如,看下面的代码:

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

当你运行的时候,你可以看到像这样的东西:

 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 

这表明,正确的说,顺序不被视为一个集合的显着特征。

还有其他方法可以在没有手动计数器的情况下进行,但对于可疑的益处来说,这是相当可观的工作。

Java 8中使用lambdas函数接口使创build新的循环抽象成为可能。 我可以通过索引和集合大小来循环集合:

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

哪些产出:

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

我实现的是:

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

可能性是无止境的。 例如,我创build了一个仅为第一个元素使用特殊函数的抽象:

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

其中正确输出逗号分隔列表:

 one,two,three,four 

我实现的是:

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

图书馆会开始做这些事情,或者你可以推出自己的东西。

恐怕这是不可能的,与foreach 。 但我可以build议你一个简单的旧式for循环

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

请注意, List接口可以访问it.nextIndex()

(编辑)

给你改变的例子:

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

Sun为Java7考虑的其中一个变化是在foreach循环中提供对内部Iterator访问。 语法将是这样的(如果这被接受):

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

这是句法糖,但显然这个function有很多要求。 但是在批准之前,您必须自己计算迭代次数,或者使用Iterator的常规循环。

Java 8引入了Iterable#forEach() / Map#forEach()方法,与“经典”for-each循环相比,对于许多Collection / Map实现来说效率更高。 但是,在这种情况下也不提供索引。 这里的技巧是在lambdaexpression式之外使用AtomicInteger 。 注意:在lambdaexpression式中使用的variables必须是有效的,这就是为什么我们不能使用普通的int

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

如果你在for-each循环中需要一个计数器,你必须自己计数。 据我所知,没有内置的柜台。

有一个“变种”pax的答案… 😉

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

有很多方法,但方法是其中之一

习惯解决scheme:

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

这实际上是Google在Guava讨论中为他们没有提供CountingIterator原因所build议的解决scheme。

我发现使用一个简单的循环与增量索引是最有效的,可靠的解决scheme,发布在这里: https : //stackoverflow.com/a/3431543/2430549

对于偶尔需要索引的情况,比如在catch子句中,我有时会使用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); } } 

我有点惊讶没有人build议以下(我承认这是一个懒惰的方法…); 如果stringArray是某种types的List,那么可以使用类似于stringArray.indexOf(S)的方法来为当前计数返回一个值。

注意:这里假定列表的元素是唯一的,或者它们是否是非唯一的并不重要(因为在这种情况下它将返回find的第一个副本的索引)。

有些情况下,这足以…