有没有办法在 Java 的 for-each 循环中访问迭代计数器?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/477550/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Is there a way to access an iteration-counter in Java's for-each loop?
提问by Kosi2801
Is there a way in Java's for-each loop
Java 的 for-each 循环中有没有办法
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
除了使用旧的和众所周知的for(int i=0; i < boundary; i++)
- 循环之外,还有构造
int i = 0;
for(String s : stringArray) {
doSomethingWith(s);
i++;
}
the only way to have such a counter available in a for-each loop?
在 for-each 循环中使用这样一个计数器的唯一方法是什么?
采纳答案by Michael Borgwardt
No, but you can provide your own counter.
不可以,但您可以提供自己的柜台。
The reason for this is that the for-each loop internally does not havea counter; it is based on the Iterableinterface, 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).
这样做的原因是,for-each循环在内部不具有一个计数器; 它基于Iterable接口,即它使用Iterator
循环遍历“集合”——它可能根本不是一个集合,实际上可能根本不是基于索引的东西(例如链表)。
回答by EricSchaefer
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.
如果在 for-each 循环中需要计数器,则必须自己计算。据我所知,没有内置柜台。
回答by paxdiablo
The easiest solution isto 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 havean index, or even have a defined order(some collections may change the order when you add or remove elements).
这样做的原因是因为没有实际保证集合中的项目(该变体对其进行for
迭代)甚至具有索引,甚至具有定义的顺序(某些集合可能会在您添加或删除元素时更改顺序)。
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 withouta manual counter but it's a fair bit of work for dubious benefit.
在没有手动计数器的情况下,还有其他方法可以做到这一点,但为了可疑的利益,这是一项相当大的工作。
回答by bruno conde
I'm afraid this isn't possible with foreach
. But I can suggest you a simple old-styled for-loops:
恐怕这是不可能的foreach
。但我可以建议你一个简单的老式 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();
}
Notice that, the Listinterface gives you access to it.nextIndex()
.
请注意,List接口允许您访问it.nextIndex()
.
(edit)
(编辑)
To your changed example:
对于您更改的示例:
for(ListIterator<String> it =l.listIterator(); it.hasNext() ;)
{
int i = it.nextIndex();
doSomethingWith(it.next(), i);
}
回答by Yuval
One of the changes Sun
is considering for Java7
is to provide access to the inner Iterator
in foreach loops. the syntax will be something like this (if this is accepted):
Sun
正在考虑的更改之一是在 foreach 循环中Java7
提供对内部的访问Iterator
。语法将是这样的(如果接受的话):
for (String str : list : it) {
if (str.length() > 100) {
it.remove();
}
}
This is syntactic sugar, but apparently a lot of requests were made for this feature. But until it is approved, you'll have to count the iterations yourself, or use a regular for loop with an Iterator
.
这是语法糖,但显然很多人都对这个功能提出了要求。但是在获得批准之前,您必须自己计算迭代次数,或者使用带有Iterator
.
回答by akuhn
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
鉴于您编写自己的Index
类和一个返回Iterable
此类的过度实例的静态方法,您可以
for (Index<String> each: With.index(stringArray)) {
each.value;
each.index;
...
}
Where the implementation of With.index
is something like
的实现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++); }
...
}
}
}
}
}
回答by J?rg
There is a "variant" to pax' answer... ;-)
pax的答案有一个“变体”...... ;-)
int i = -1;
for(String s : stringArray) {
doSomethingWith(s, ++i);
}
回答by Rik
I'm a little surprised no-one suggested the following (I admit it's a lazy approach...); If stringArray is a List of some sort, you could use something like stringArray.indexOf(S) to return a value for the current count.
我有点惊讶没有人提出以下建议(我承认这是一种懒惰的方法......);如果 stringArray 是某种列表,您可以使用类似 stringArray.indexOf(S) 之类的东西来返回当前计数的值。
Note: this assumes that the elements of the List are unique, or that it doesn't matter if they are non-unique (because in that case it will return the index of the first copy found).
注意:这假设 List 的元素是唯一的,或者它们是否不唯一都没有关系(因为在这种情况下它将返回找到的第一个副本的索引)。
There are situations in which that would be sufficient...
在某些情况下,这就足够了......
回答by James Scriven
Using lambdasand functional interfacesin Java 8makes creating new loop abstractions possible. I can loop over a collection with the index and the collection size:
在Java 8 中使用lambda和函数式接口使得创建新的循环抽象成为可能。我可以使用索引和集合大小遍历集合:
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.
图书馆会开始出现来做这些事情,或者你可以自己动手。
回答by rmuller
Java 8 introduced the Iterable#forEach()
/ Map#forEach()
method, which is more efficient for many Collection
/ Map
implementations compared to the "classical" for-each loop. However, also in this case an index is not provided. The trick here is to use AtomicInteger
outside the lambda expression. Note: variables used within the lambda expression must be effectively final, that is why we cannot use an ordinary int
.
Java 8 引入了Iterable#forEach()
/Map#forEach()
方法,与“经典”的 for-each 循环相比,它对许多Collection
/Map
实现更有效。然而,在这种情况下也不提供索引。这里的技巧是AtomicInteger
在 lambda 表达式之外使用。注意:在 lambda 表达式中使用的变量必须是有效的 final,这就是为什么我们不能使用普通的int
.
final AtomicInteger indexHolder = new AtomicInteger();
map.forEach((k, v) -> {
final int index = indexHolder.getAndIncrement();
// use the index
});