详细来说,“foreach”是如何实现的? Java 中的循环工作?
考虑一下:
List<String> someList = new ArrayList<>();
// add "monkey", "donkey", "skeleton key" to someList
for (String item : someList) {
System.out.println(item);
}
如果不使用 foreach 语法,等效的 for
循环会是什么样子?
刚接触 Java 的人在尝试使用新样式的 foreach 循环修改原始数据时通常会遇到问题。 使用 为什么在 foreach 循环中分配给迭代变量不会更改基础数据? 来关闭有关该常见问题的重复项。 请注意,具有类似结构的其他语言通常也存在相同的问题; 例如,对于 Python 中的同一问题,请参阅 为什么修改迭代变量不会影响后续迭代?。
Consider:
List<String> someList = new ArrayList<>();
// add "monkey", "donkey", "skeleton key" to someList
for (String item : someList) {
System.out.println(item);
}
What would the equivalent for
loop look like without using the for each syntax?
People new to Java commonly encounter issues when trying to modify the original data using the new style foreach loop. Use Why doesn't assigning to the iteration variable in a foreach loop change the underlying data? to close duplicates about that common problem. Note that other languages with analogous constructs generally have the same issue; for example, see Why doesn't modifying the iteration variable affect subsequent iterations? for the same issue in Python.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(29)
使用forEach:
响应:
PS:您需要一个数组(
int[]numbers
),并import java.util.Arrays;
Using forEach:
Response:
PS: You need a Array (
int[] numbers
), andimport java.util.Arrays;
如 JLS,for-each 循环可以有两种形式:
如果表达式的类型是
Iterable
的子类型,则翻译如下:如果表达式必须具有数组类型
T[]
,则:Java 8 引入了流,通常在数据集大小合适的情况下表现更好。 我们可以将它们用作:
As defined in JLS, a for-each loop can have two forms:
If the type of expression is a subtype of
Iterable
then translation is as:If the expression necessarily has an array type
T[]
then:Java 8 has introduced streams which perform generally better with a decent size dataset. We can use them as:
Java 中的 for-each 循环使用底层迭代器机制。 所以它与以下内容相同:
The for-each loop in Java uses the underlying iterator mechanism. So it's identical to the following:
Java for every 循环(又名增强型 for 循环)是 for 的简化版本环形。 优点是需要编写的代码较少,需要管理的变量也较少。 缺点是您无法控制步长值,也无法访问循环体内的循环索引。
当步长值是简单的 1 增量并且您只需要访问当前循环元素时,最好使用它们。 例如,如果您需要循环遍历数组或集合中的每个元素,而不需要查看当前元素的前方或后方。
没有循环初始化,没有布尔条件,并且步长值是隐式的并且是简单的增量。 这就是为什么它们被认为比常规 for 循环简单得多。
增强型 for 循环遵循以下执行顺序:
1) 循环体
2) 从步骤 1 开始重复,直到遍历完整个数组或集合
示例 – 整数数组
currentValue 变量保存循环中的当前值intArray 数组。 请注意,没有明确的步长值 - 它始终以 1 为增量。
冒号可以被认为表示“in”。 因此,增强的 for 循环声明指出:循环 intArray 并将当前数组 int 值存储在 currentValue 变量中。
输出:
示例 - 字符串数组
我们可以使用 for-each 循环来迭代字符串数组。 循环声明指出:循环遍历 myStrings String 数组并将当前 String 值存储在 currentString 变量中。
输出:
示例 – 列表
增强的 for 循环还可用于迭代 java.util.List,如下所示:
循环声明指出:循环遍历 myList 字符串列表并存储当前列表值 <在 currentItem 变量中。
输出:
示例 – Set
增强的 for 循环还可用于迭代 java.util.Set,如下所示:
循环声明指出:循环遍历 mySet 字符串集并存储当前 Set 值 <在 currentItem 变量中。 请注意,由于这是一个 Set,因此不会存储重复的 String 值。
输出:
来源:Java 中的循环 - 终极指南
The Java for each loop (aka enhanced for loop) is a simplified version of a for loop. The advantage is that there is less code to write and less variables to manage. The downside is that you have no control over the step value and no access to the loop index inside the loop body.
They are best used when the step value is a simple increment of 1 and when you only need access to the current loop element. For example, if you need to loop over every element in an array or Collection without peeking ahead or behind the current element.
There is no loop initialization, no boolean condition and the step value is implicit and is a simple increment. This is why they are considered so much simpler than regular for loops.
Enhanced for loops follow this order of execution:
1) loop body
2) repeat from step 1 until entire array or collection has been traversed
Example – Integer Array
The currentValue variable holds the current value being looped over in the intArray array. Notice there’s no explicit step value – it’s always an increment by 1.
The colon can be thought of to mean “in”. So the enhanced for loop declaration states: loop over intArray and store the current array int value in the currentValue variable.
Output:
Example – String Array
We can use the for-each loop to iterate over an array of strings. The loop declaration states: loop over myStrings String array and store the current String value in the currentString variable.
Output:
Example – List
The enhanced for loop can also be used to iterate over a java.util.List as follows:
The loop declaration states: loop over myList List of Strings and store the current List value in the currentItem variable.
Output:
Example – Set
The enhanced for loop can also be used to iterate over a java.util.Set as follows:
The loop declaration states: loop over mySet Set of Strings and store the current Set value in the currentItem variable. Notice that since this is a Set, duplicate String values are not stored.
Output:
Source: Loops in Java – Ultimate Guide
每个的构造对于数组也有效。 例如,
本质上相当于
So,总体总结:
[nsayer]以下是较长的正在发生的事情的形式:
[丹尼斯·布埃诺]
The construct for each is also valid for arrays. e.g.
which is essentially equivalent of
So, overall summary:
[nsayer] The following is the longer form of what is happening:
[Denis Bueno]
for-each 循环,添加在 < a href="http://docs.oracle.com/javase/1.5.0/docs/guide/language/index.html" rel="noreferrer">Java 5 (也称为“增强的 for 循环"),相当于使用
java.lang. util.Iterator
——它是同一事物的语法糖。 因此,在逐个按顺序读取每个元素时,应始终选择 for-each 而不是迭代器,因为它更方便、更简洁。For-each
Iterator
在某些情况下,您必须直接使用
Iterator
。 例如,尝试在使用 for-each 时删除元素会(将?)导致ConcurrentModificationException
。For-each 与 for-loop:基本差异
for-loop 和 for-each 之间唯一的实际差异是,对于可索引对象,您无权访问索引。 需要基本 for 循环的示例:
虽然您可以使用 for-each 手动创建单独的索引 int 变量,但
...不推荐,因为 variable-scope 并不理想,基本的
for
循环只是此用途的标准和预期格式案件。For-each 与 for-loop:性能
访问集合时,for-each 是 比基本
for
循环的数组访问快得多。 然而,当访问数组时(至少对于原始数组和包装数组而言),通过索引进行访问要快得多。计算原始 int 数组的迭代器和索引访问之间的差异
在访问
int
或Integer
数组时,索引比迭代器快 23-40%。 以下是本文底部测试类的输出,它将 100 个元素的 Primitive-int 数组中的数字相加(A 是迭代器,B 是索引):我还针对
Integer
运行了这个输出 数组,索引仍然是明显的赢家,但速度只快了 18% 到 25%。对于集合,迭代器比索引更快。
但是,对于
Integers
的List
,迭代器显然是赢家。 只需将测试类中的 int-array 更改为:并对测试函数进行必要的更改(将
int[]
更改为List
,length
到size()
等):在一项测试中,它们几乎是等效的,但对于集合,迭代器获胜。
*这篇文章基于我在 Stack Overflow 上写的两个答案:
Java 中 for-each 循环的使用和语法
我应该使用迭代器还是for循环来迭代?
更多信息:for-each 循环还是迭代器哪个更高效?
我创建的完整测试类
比较了所需时间-阅读此后的-to-do-any-two-things类关于 Stack Overflow 的问题:
The for-each loop, added in Java 5 (also called the "enhanced for loop"), is equivalent to using a
java.util.Iterator
--it's syntactic sugar for the same thing. Therefore, when reading each element, one by one and in order, a for-each should always be chosen over an iterator, as it is more convenient and concise.For-each
Iterator
There are situations where you must use an
Iterator
directly. For example, attempting to delete an element while using a for-each can (will?) result in aConcurrentModificationException
.For-each vs. for-loop: Basic differences
The only practical difference between for-loop and for-each is that, in the case of indexable objects, you do not have access to the index. An example when the basic for-loop is required:
Although you could manually create a separate index int-variable with for-each,
...it is not recommended, since variable-scope is not ideal, and the basic
for
loop is simply the standard and expected format for this use case.For-each vs. for-loop: Performance
When accessing collections, a for-each is significantly faster than the basic
for
loop's array access. When accessing arrays, however--at least with primitive and wrapper-arrays--access via indexes is dramatically faster.Timing the difference between iterator and index access for primitive int-arrays
Indexes are 23-40 percent faster than iterators when accessing
int
orInteger
arrays. Here is the output from the testing class at the bottom of this post, which sums the numbers in a 100-element primitive-int array (A is iterator, B is index):I also ran this for an
Integer
array, and indexes are still the clear winner, but only between 18 and 25 percent faster.For collections, iterators are faster than indexes
For a
List
ofIntegers
, however, iterators are the clear winner. Just change the int-array in the test-class to:And make the necessary changes to the test-function (
int[]
toList<Integer>
,length
tosize()
, etc.):In one test they're almost equivalent, but with collections, iterator wins.
*This post is based on two answers I wrote on Stack Overflow:
Uses and syntax for for-each loop in Java
Should I use an Iterator or a forloop to iterate?
Some more information: Which is more efficient, a for-each loop, or an iterator?
The full testing class
I created this compare-the-time-it-takes-to-do-any-two-things class after reading this question on Stack Overflow:
它看起来像这样。 非常粗暴。
每个的很好的文章“nofollow noreferrer">Sun 文档。
It would look something like this. Very crufty.
There is a good writeup on for each in the Sun documentation.
请注意,如果您需要在循环中使用
i.remove();
,或者以某种方式访问实际的迭代器,则不能使用for ( : )
习惯用法,因为实际的迭代器只是推断出来的。正如 Denis Bueno 所指出的,此代码适用于任何实现
可迭代
接口。如果 for (:) 习惯用法的右侧是数组而不是 Iterable 对象,则内部代码使用 int 索引计数器并检查
array.length 代替。 请参阅 Java 语言规范< /a>.
Note that if you need to use
i.remove();
in your loop, or access the actual iterator in some way, you cannot use thefor ( : )
idiom, since the actual iterator is merely inferred.As was noted by Denis Bueno, this code works for any object that implements the
Iterable
interface.If the right-hand side of the
for (:)
idiom is an array rather than anIterable
object, the internal code uses an int index counter and checks againstarray.length
instead. See the Java Language Specification.在 Java 8 之前,您需要使用以下内容:
但是,随着 Java 8 中引入 Streams,您可以使用更少的语法完成相同的操作。 例如,对于您的
someList
您可以执行以下操作:您可以找到有关流的更多信息 此处。
Prior to Java 8, you need to use the following:
However, with the introduction of Streams in Java 8 you can do same thing in much less syntax. For example, for your
someList
you can do:You can find more about streams here.
它通过消除所有基本的循环混乱来为您的代码增添美感。 它使您的代码看起来很干净,如下所示。
普通
for
循环:使用 for-each:
for-each 是实现 的集合上的构造>迭代器。 请记住,您的集合应该实现Iterator; 否则你不能将它与 for-each 一起使用。
以下行读作“for every TimerTask t in list.”
在 for-each 情况下出错的可能性较小。 您不必担心初始化迭代器或初始化循环计数器并终止它(在存在错误范围的情况下)。
It adds beauty to your code by removing all the basic looping clutter. It gives a clean look to your code, justified below.
Normal
for
loop:Using for-each:
for-each is a construct over a collection that implements Iterator. Remember that, your collection should implement Iterator; otherwise you can't use it with for-each.
The following line is read as "for each TimerTask t in list."
There is less chance for errors in case of for-each. You don't have to worry about initializing the iterator or initializing the loop counter and terminating it (where there is scope for errors).
另请注意,在原始问题中使用“foreach”方法确实有一些限制,例如无法在迭代期间从列表中删除项目。
新的 for 循环更易于阅读,并且无需单独的迭代器,但仅在只读迭代过程中真正可用。
Also note that using the "foreach" method in the original question does have some limitations, such as not being able to remove items from the list during the iteration.
The new for-loop is easier to read and removes the need for a separate iterator, but is only really usable in read-only iteration passes.
forEach 的替代方案,以避免“foreach”:
变体 1(普通):
变体 2(并行执行(更快)):
An alternative to forEach in order to avoid your "for each":
Variant 1 (plain):
Variant 2 (parallel execution (faster)):
使用较旧的 Java 版本,包括 Java 7,您可以使用
foreach
循环如下。以下是在 Java 中使用 for every 循环的最新方法;8(使用
forEach
+ lambda 表达式或方法引用循环列表)。Lambda
方法参考
有关更多信息,请参阅"Java 8 forEach 示例”。
Using older Java versions, including Java 7, you can use a
foreach
loop as follows.The following is the very latest way of using a for each loop in Java 8 (loop a List with
forEach
+ lambda expression or method reference).Lambda
Method reference
For more information, refer to "Java 8 forEach examples".
这是一个等价的表达式。
Here's an equivalent expression.
维基百科中提到的 foreach 循环的概念强调如下:
因此,foreach循环的概念描述了该循环不使用任何显式计数器,这意味着不需要使用索引来遍历列表,从而使用户避免了差一错误。 为了描述这种差一错误的一般概念,让我们举一个使用索引遍历列表的循环示例。
但假设如果列表以索引 1 开头,则此循环将引发异常,因为它将在索引 0 处找不到元素,并且此错误称为偏一错误。 因此,为了避免这种差一错误,使用了 foreach 循环的概念。 可能还有其他优点,但这就是我认为使用 foreach 循环的主要概念和优点。
The concept of a foreach loop as mentioned in Wikipedia is highlighted below:
So the concept of a foreach loop describes that the loop does not use any explicit counter which means that there is no need of using indexes to traverse in the list thus it saves user from off-by-one error. To describe the general concept of this off-by-one error, let us take an example of a loop to traverse in a list using indexes.
But suppose if the list starts with index 1 then this loop is going to throw an exception as it will found no element at index 0 and this error is called an off-by-one error. So to avoid this off-by-one error the concept of a foreach loop is used. There may be other advantages too, but this is what I think is the main concept and advantage of using a foreach loop.
在 Java 8 中,他们引入了 forEach。 使用List、Maps可以循环。
使用 foreach 循环列表
或
使用 foreach 循环地图
或
In Java 8, they introduced forEach. Using it List, Maps can be looped.
Loop a List using for each
or
Loop a Map using for each
or
Java“for-each”循环构造将允许对两种类型的对象进行迭代:
T[]
(任何类型的数组)java.lang.Iterable
T[]
(任何类型的数组)Iterable
接口只有一个方法:
Iterator。 迭代器()。 这适用于
Collection
类型的对象,因为Collection
接口扩展了Iterable
。The Java "for-each" loop construct will allow iteration over two types of objects:
T[]
(arrays of any type)java.lang.Iterable<T>
The
Iterable<T>
interface has only one method:Iterator<T> iterator()
. This works on objects of typeCollection<T>
because theCollection<T>
interface extendsIterable<T>
.在 Java 8 功能中,您可以使用以下功能:
输出
In Java 8 features you can use this:
Output
foreach 循环语法为:
示例:
输出:
警告:您可以使用 foreach 循环访问数组元素,但不能初始化它们。 为此,请使用原始的
for
循环。警告:您必须将数组的类型与其他对象相匹配。
如果您想编辑元素,请使用原始的
for
循环,如下所示:现在,如果我们将 s 转储到控制台,我们会得到:
A foreach loop syntax is:
Example:
Output:
WARNING: You can access array elements with the foreach loop, but you can NOT initialize them. Use the original
for
loop for that.WARNING: You must match the type of the array with the other object.
If you want to edit elements, use the original
for
loop like this:Now if we dump s to the console, we get:
nsayer 的答案暗示了这一点,但它是值得注意的是,当“someList”是实现 java.lang.Iterable 的任何东西时,OP 的 for(..) 语法将起作用——它不必是一个列表,或者来自 java 的一些集合.util。 因此,即使是您自己的类型也可以与此语法一起使用。
It's implied by nsayer's answer, but it's worth noting that the OP's for(..) syntax will work when "someList" is anything that implements java.lang.Iterable -- it doesn't have to be a list, or some collection from java.util. Even your own types, therefore, can be used with this syntax.
这是一个不假设您了解 Java 迭代器的答案。 它不太精确,但对教育很有用。
在编程时,我们经常编写如下所示的代码:
foreach 语法允许以更自然且语法干扰更少的方式编写这种常见模式。
此外,此语法对于不支持数组索引但实现了 Java Iterable 接口的对象(例如列表或集合)有效。
Here is an answer which does not assume knowledge of Java Iterators. It is less precise, but it is useful for education.
While programming we often write code that looks like the following:
The foreach syntax allows this common pattern to be written in a more natural and less syntactically noisy way.
Additionally this syntax is valid for objects such as Lists or Sets which do not support array indexing, but which do implement the Java Iterable interface.
正如许多好的答案所说,如果对象想要使用
for-each
循环,则它必须实现Iterable 接口
。我将发布一个简单的示例,并尝试以不同的方式解释
for-each
循环的工作原理。for-each
循环示例:然后,如果我们使用
javap
反编译这个类,我们将得到这个字节码样本:从样本的最后一行可以看到,编译器会在编译时自动将
for-each
关键字的使用转换为Iterator
的使用。 这可以解释为什么没有实现 Iterable 接口的对象在尝试使用 for-each 循环时会抛出异常。As so many good answers said, an object must implement the
Iterable interface
if it wants to use afor-each
loop.I'll post a simple example and try to explain in a different way how a
for-each
loop works.The
for-each
loop example:Then, if we use
javap
to decompile this class, we will get this bytecode sample:As we can see from the last line of the sample, the compiler will automatically convert the use of
for-each
keyword to the use of anIterator
at compile time. That may explain why object, which doesn't implement theIterable interface
, will throw anException
when it tries to use thefor-each
loop.代码是:
The code would be:
我认为这会起作用:
I think this will work:
这看起来很疯狂,但是嘿它有效
这有效。 魔法
This looks crazy but hey it works
This works. Magic
Java for-each 习惯用法只能应用于*Iterable 类型的数组或对象。 这个习惯用法是隐式的,因为它真正由迭代器支持。 迭代器由程序员编程,通常使用整数索引或节点(取决于数据结构)来跟踪其位置。 从理论上讲,它比常规的 for 循环慢,至少对于数组和列表等“线性”结构来说是这样,但它提供了更好的抽象。
The Java for-each idiom can only be applied to arrays or objects of type *Iterable. This idiom is implicit as it truly backed by an Iterator. The Iterator is programmed by the programmer and often uses an integer index or a node (depending on the data structure) to keep track of its position. On paper it is slower than a regular for-loop, a least for "linear" structures like arrays and Lists but it provides greater abstraction.
正如许多其他答案正确指出的那样,foreach循环只是相同旧for循环的语法糖,编译器将其转换为相同旧for< /em> 循环。
javac (OpenJDK) 有一个开关,
-XD-printflat
,它会生成一个删除了所有语法糖的 Java 文件。 完整的命令如下所示:因此,让我们删除语法糖
为了回答这个问题,我创建了一个文件并为每个文件编写了两个版本的 ,一个使用 array,另一个使用 array一个列表。 我的 Java 文件如下所示:
当我使用上述开关编译该文件时,我得到了以下输出。
您可以看到,与其他语法糖(自动装箱)一起,foreach 循环已更改为简单循环。
As many of other answers correctly state, the for each loop is just syntactic sugar over the same old for loop and the compiler translates it to the same old for loop.
javac (OpenJDK) has a switch,
-XD-printflat
, which generates a Java file with all the syntactic sugar removed. The complete command looks like this:So let’s remove the syntactical sugar
To answer this question, I created a file and wrote two versions of for each, one with array and another with a list. My Java file looked like this:
When I
compiled
this file with above switch, I got the following output.You can see that along with the other syntactic sugar (Autoboxing), for each loops got changed to simple loops.