在 Java 8 中,为什么没有为数组提供 Iterable 的 forEach 方法?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/35518471/
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
In Java 8, why were Arrays not given the forEach method of Iterable?
提问by Kedar Mhaswade
I must be missing something here.
我一定在这里遗漏了一些东西。
In Java 5, the "for-each loop" statement (also called the enhanced for loop)was introduced. It appears that it was introduced mainly to iterate through Collections. Any collection (or container) class that implements the Iterable
interface is eligible for iteration using the "for-each loop". Perhaps for historic reasons, the Java arrays did not implement the Iterable interface. But since arrays were/are ubiquitous, javac
would accept the use of for-each loop on arrays (generating bytecode equivalent to a traditional for loop).
在 Java 5 中,引入了“for-each 循环”语句(也称为增强的 for 循环)。似乎引入它主要是为了遍历Collections。任何实现该Iterable
接口的集合(或容器)类都可以使用“for-each 循环”进行迭代。也许由于历史原因,Java 数组没有实现 Iterable 接口。但是由于数组无处不在,因此javac
可以接受在数组上使用 for-each 循环(生成相当于传统 for 循环的字节码)。
In Java 8, the forEach
methodwas added to the Iterable
interface as a defaultmethod. This made passing lambda expressions to collections (while iterating) possible (e.g. list.forEach(System.out::println)
). But again, arrays don't enjoy this treatment. (I understand that there are workarounds).
在 Java 8 中,该forEach
方法Iterable
作为默认方法添加到接口中。这使得将 lambda 表达式传递给集合(在迭代时)成为可能(例如list.forEach(System.out::println)
)。但同样,阵列不享受这种待遇。(我知道有解决方法)。
Are there technical reasons why javac couldn't be enhanced to accept arrays in forEach, just like it accepts them in the enhanced for loop? It appears that code generation would be possible without requiring that arrays implement Iterable
. Am I being naive?
javac 无法增强以在 forEach 中接受数组,就像它在增强的 for 循环中接受数组一样,是否存在技术原因?似乎无需数组实现就可以生成代码Iterable
。我很天真吗?
This is especially important for a newcomer to the language who rather naturally uses arrays because of their syntactical ease. It's hardly natural to switch to Lists and use Arrays.asList(1, 2, 3)
.
这对于该语言的新手来说尤其重要,因为数组的语法很简单,所以他们很自然地使用数组。切换到 Lists 并使用Arrays.asList(1, 2, 3)
.
采纳答案by Stuart Marks
There are a bunch of special cases in the Java language and in the JVM for arrays. Arrays have an API, but it's barely visible. It is as if arrays are declared to have:
对于数组,Java 语言和 JVM 中有很多特殊情况。数组有一个 API,但它几乎不可见。就好像数组被声明为具有:
implements Cloneable, Serializable
public final int length
public T[] clone()
whereT
is the array's component type
implements Cloneable, Serializable
public final int length
public T[] clone()
T
数组的组件类型在哪里
However, these declarations aren't visible in any source code anywhere. See JLS 4.10.3and JLS 10.7for explanations. Cloneable
and Serializable
are visible via reflection, and are returned by a call to
但是,这些声明在任何源代码中的任何地方都不可见。有关解释,请参阅JLS 4.10.3和JLS 10.7。Cloneable
并且Serializable
通过反射可见,并通过调用返回
Object[].class.getInterfaces()
Perhaps surprisingly, the length
field and the clone()
method aren't visible reflectively. The length
field isn't a field at all; using it turns into a special arraylength
bytecode. A call to clone()
results in an actual virtual method call, but if the receiver is an array type, this is handled specially by the JVM.
也许令人惊讶的是,该length
字段和clone()
方法在反射中是不可见的。这个length
领域根本不是一个领域;使用它会变成一个特殊的arraylength
字节码。调用会clone()
导致实际的虚方法调用,但如果接收者是数组类型,则由 JVM 专门处理。
Notably, though, array classes do not implement the Iterable
interface.
但值得注意的是,数组类不实现该Iterable
接口。
When the enhanced-for loop ("for-each") was added in Java SE 5, it supported two different cases for the right-hand-side expression: an Iterable
or an array type (JLS 14.14.2). The reason is that Iterable
instances and arrays are handled completely differently by the enhanced-for statement. That section of the JLS gives the full treatment, but put more simply, the situation is as follows.
当在 Java SE 5 中添加增强的 for 循环(“for-each”)时,它支持右侧表达式的两种不同情况:一个Iterable
或数组类型(JLS 14.14.2)。原因是Iterable
增强的for 语句对实例和数组的处理方式完全不同。JLS的那部分给出了完整的处理,但更简单地说,情况如下。
For an Iterable<T> iterable
, the code
对于Iterable<T> iterable
,代码
for (T t : iterable) {
<loop body>
}
is syntactic sugar for
是语法糖
for (Iterator<T> iterator = iterable.iterator(); iterator.hasNext(); ) {
t = iterator.next();
<loop body>
}
For an array T[]
, the code
对于数组T[]
,代码
for (T t : array) {
<loop body>
}
is syntactic sugar for
是语法糖
int len = array.length;
for (int i = 0; i < len; i++) {
t = array[i];
<loop body>
}
Now, why was it done this way? It would certainly be possible for arrays to implement Iterable
, since they implement other interfaces already. It would also be possible for the compiler to synthesize an Iterator
implementation that's backed by an array. (There is precedent for this. The compiler already synthesizes the static values()
and valueOf()
methods that are automatically added to every enum
class, as described in JLS 8.9.3.)
那么,为什么要这样做呢?数组当然可以实现Iterable
,因为它们已经实现了其他接口。编译器也可以合成Iterator
由数组支持的实现。(这有先例。编译器已经合成了自动添加到每个类的静态values()
和valueOf()
方法enum
,如JLS 8.9.3 中所述。)
But arrays are a very low-level construct, and accessing an array by an int
value is expected to be extremely inexpensive operation. It's quite idiomatic to run a loop index from 0
to an array's length, incrementing by one each time. The enhanced-for loop on an array does exactly that. If the enhanced-for loop over an array were implemented using the Iterable
protocol, I think most people would be unpleasantly surprised to discover that looping over an array involved an initial method call and memory allocation (creating the Iterator
), followed by two method calls per loop iteration.
但是数组是一个非常低级的结构,通过一个int
值访问一个数组预计是非常便宜的操作。运行一个循环索引从0
到数组的长度是非常惯用的,每次递增 1。数组上的增强 for 循环正是这样做的。如果使用该Iterable
协议实现对数组的增强 for 循环,我认为大多数人会惊讶地发现,在数组上循环涉及初始方法调用和内存分配(创建Iterator
),然后是每个循环的两个方法调用迭代。
So when default methods were added to Iterable
in Java 8, this didn't affect arrays at all.
因此,当Iterable
在 Java 8中添加默认方法时,这根本不会影响数组。
As others have noted, if you have an array of int
, long
, double
, or of reference type, it's possible to turn this into a stream using one of the Arrays.stream()
calls. This provides access to map()
, filter()
, forEach()
, etc.
正如其他人所指出的,如果你有一个数组int
,long
,double
,或引用类型,它可能把它变成使用的一个流Arrays.stream()
调用。这提供了访问map()
,filter()
,forEach()
,等。
It would be nice, though, if the special cases in the Java language and JVM for arrays were replaced by realconstructs (along with fixing a bunch of other array-related problems, such as poor handling of 2+ dimensional arrays, the 2^31 length limitation, and so forth). This is the subject of the "Arrays 2.0" investigation being led by John Rose. See John's talk at JVMLS 2012 (video, slides). The ideas relevant to this discussion include introduction of an actual interface for arrays, to allow libraries to interpose element access, to support additional operations such as slicing and copying, and so forth.
不过,如果 Java 语言和 JVM 中数组的特殊情况被真正的构造所取代(同时修复一堆其他与数组相关的问题,例如对 2+ 维数组的处理不当,2^ 31 长度限制,等等)。这是由 John Rose 领导的“Arrays 2.0”调查的主题。请参阅 John 在 JVMLS 2012 上的演讲(视频、幻灯片)。与本次讨论相关的想法包括引入数组的实际接口,允许库插入元素访问,支持额外的操作,如切片和复制,等等。
Note that all of this is investigation and future work. There is nothing from these array enhancements that is committed in the Java roadmap for any release, as of this writing (2016-02-23).
请注意,所有这些都是调查和未来的工作。在撰写本文时 (2016 年 2 月 23 日),任何版本的 Java 路线图中都没有提交这些数组增强功能。
回答by Tagir Valeev
Suppose the special code will be added into java compiler to handle forEach
.
Then many similar questions could be asked.
Why we cannot write myArray.fill(0)
? Or myArray.copyOfRange(from, to)
? Or myArray.sort()
?
myArray.binarySearch()
? myArray.stream()
? Practically every static method in Arrays
interface could be converted into the corresponding method of the "array class". Why should JDK developers stop on
myArray.forEach()
? Note however that every such method must be added not only into classlib specification,
but into Java Language Specification which is far more stable and conservative. Also this would mean that not only
the implementation of such methods would become part of specification, but also classes like java.util.function.Consumer
should be explicitly mentioned in JLS (which is the argument of proposed forEach
method).
Also note that new consumers would be necessary to add to the
standard library like FloatConsumer
, ByteConsumer
, etc. for the corresponding array types.
Currently the JLS rarely refers to the types outside of java.lang
package (with some notable exceptions
like java.util.Iterator
). This implies some stability layer. The proposed change is too drastic for Java language.
假设将特殊代码添加到 java 编译器中来处理forEach
. 然后可以问很多类似的问题。为什么我们不能写myArray.fill(0)
?或者myArray.copyOfRange(from, to)
?或者myArray.sort()
?
myArray.binarySearch()
? myArray.stream()
? 实际上,Arrays
接口中的每个静态方法都可以转换为“数组类”的相应方法。为什么 JDK 开发人员应该停下来
myArray.forEach()
?但是请注意,每个这样的方法不仅必须添加到 classlib 规范中,而且还必须添加到更加稳定和保守的 Java 语言规范中。这也意味着不仅此类方法的实现将成为规范的一部分,而且还应java.util.function.Consumer
在 JLS 中明确提及类似的类(这是提议的论点)forEach
方法)。另外请注意,新的消费者将有必要增加类似标准库FloatConsumer
,ByteConsumer
等了相应的数组类型。目前 JLS 很少引用java.lang
包之外的类型(有一些值得注意的例外,如java.util.Iterator
)。这意味着一些稳定层。提议的更改对于 Java 语言来说过于激烈。
Also note that currently we have one method which could be called for arrays directly (and which implementation differs
from the java.lang.Object
): it's clone()
method. It actually adds some dirty parts into javac and even JVM
as it must be handled specially everywhere. This causes bugs (e.g. method references were incorrectly handled in Java 8 JDK-8056051). Adding more similar complexity into javac may introduce
even more similar bugs.
另请注意,目前我们有一种可以直接为数组调用的方法(其实现与 不同java.lang.Object
):它的clone()
方法。它实际上在 javac 甚至 JVM 中添加了一些脏部分,因为它必须在任何地方进行特殊处理。这会导致错误(例如,Java 8 JDK-8056051 中的方法引用处理不当)。将更多类似的复杂性添加到 javac 中可能会引入更多类似的错误。
Such feature will probably be implemented in some not so near future as a part of Arrays 2.0 initiative. The idea is to introduce some superclass for arrays which will be located in class library, so new methods could be added just by writing normal java code without tweaking javac/JVM. However, this is also very hard feature as arrays are always treated specially in Java, and, as far as I know it's unknown yet whether it will be implemented and when.
这种特性可能会在不久的将来作为Arrays 2.0 计划的一部分实施 。这个想法是为位于类库中的数组引入一些超类,因此只需编写普通的 java 代码就可以添加新方法,而无需调整 javac/JVM。然而,这也是一个非常困难的特性,因为在 Java 中总是对数组进行特殊处理,而且据我所知,它是否会实现以及何时实现还不得而知。