Ked*_*ade 47 java arrays foreach javac java-8
我必须在这里遗漏一些东西.
在Java 5中,引入了"for-each loop"语句(也称为增强for循环).它似乎主要是为了遍历集合而引入的.任何实现该Iterable接口的集合(或容器)类都可以使用"for-each循环"进行迭代.也许出于历史原因,Java阵列没有实现Iterable接口.但是由于数组无处不在,所以javac会接受在数组上使用for-each循环(生成相当于传统for循环的字节码).
在Java 8中,该forEach方法Iterable作为默认方法添加到接口.这使得将lambda表达式传递给集合(迭代时)成为可能(例如list.forEach(System.out::println)).但同样,阵列不喜欢这种治疗方法.(我知道有解决方法).
有没有技术上的原因为什么javac无法增强以接受forEach中的数组,就像它在增强的for循环中接受它们一样?似乎可以在不要求阵列实现的情况下生成代码Iterable.我天真吗?
这对于自然使用数组的语言的新手来说尤其重要,因为它们的语法容易.切换到列表和使用是不自然的Arrays.asList(1, 2, 3).
Stu*_*rks 44
Java语言和JVM中有许多特殊情况.数组有一个API,但几乎看不到.就像声明数组有:
implements Cloneable, Serializablepublic final int lengthpublic T[] clone()T数组的组件类型在哪里但是,这些声明在任何地方的任何源代码中都不可见.有关说明,请参阅JLS 4.10.3和JLS 10.7.Cloneable并Serializable通过反射可见,并通过调用返回
Object[].class.getInterfaces()
Run Code Online (Sandbox Code Playgroud)
也许令人惊讶的是,该length领域和clone()方法不可反映.该length领域根本不是一个领域; 使用它变成一个特殊的arraylength字节码.调用clone()导致实际的虚方法调用,但如果接收器是数组类型,则由JVM专门处理.
但值得注意的是,数组类不实现Iterable接口.
当在Java SE 5中添加了增强型for循环("for-each")时,它支持右侧表达式的两种不同情况:a Iterable或数组类型(JLS 14.14.2).原因是Iterable增强for语句完全不同地处理实例和数组.JLS的那一部分提供了全面的处理,但更简单地说,情况如下.
对于Iterable<T> iterable,代码
for (T t : iterable) {
<loop body>
}
Run Code Online (Sandbox Code Playgroud)
是语法糖
for (Iterator<T> iterator = iterable.iterator(); iterator.hasNext(); ) {
t = iterator.next();
<loop body>
}
Run Code Online (Sandbox Code Playgroud)
对于数组T[],代码
for (T t : array) {
<loop body>
}
Run Code Online (Sandbox Code Playgroud)
是语法糖
int len = array.length;
for (int i = 0; i < len; i++) {
t = array[i];
<loop body>
}
Run Code Online (Sandbox Code Playgroud)
现在,为什么这样做?阵列实际上是可能的Iterable,因为它们已经实现了其他接口.编译器也可以合成Iterator由数组支持的实现.(有先例.编译器已经合成了自动添加到每个类的静态values()和valueOf()方法enum,如JLS 8.9.3中所述.)
但是数组是一个非常低级的构造,并且通过int值访问数组预计是非常便宜的操作.从循环索引0到数组的长度运行是非常惯用的,每次递增1.数组上的增强for循环就是这样.如果使用Iterable协议实现了数组上的增强for循环,我想大多数人会惊讶地发现循环数组涉及初始方法调用和内存分配(创建Iterator),然后每个循环两个方法调用迭代.
因此,当Iterable在Java 8 中添加默认方法时,这根本不会影响数组.
正如其他人所指出的,如果你有一个数组int,long,double,或引用类型,它可能把它变成使用的一个流Arrays.stream()调用.这提供了访问map(),filter(),forEach(),等.
但是,如果Java语言中的特殊情况和数组的JVM被实际构造所取代(以及修复一堆其他与数组相关的问题,例如2维数组的处理能力差,那么这将是很好的,2 ^ 31长度限制,等等).这是John Rose领导的"Arrays 2.0"调查的主题.请参阅John在JVMLS 2012上的演讲(视频,幻灯片).与此讨论相关的想法包括引入数组的实际接口,允许库插入元素访问,支持切片和复制等其他操作.
请注意,所有这些都是调查和未来的工作.在撰写本文时(2016-02-23),在任何版本的Java路线图中都没有提供这些数组增强功能.
假设将特殊代码添加到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语言,建议的更改过于激烈.
还要注意,目前我们有一个方法可以直接调用数组(和哪个实现不同java.lang.Object):它的clone()方法.它实际上将一些脏的部分添加到javac甚至JVM中,因为它必须在任何地方特别处理.这会导致错误(例如,在Java 8 JDK-8056051中错误地处理了方法引用).在javac中添加更多类似的复杂性可能会引入更多类似的错误.
作为Arrays 2.0计划的一部分,这样的功能可能会在不久的将来实现 .我们的想法是为将要位于类库中的数组引入一些超类,因此只需编写普通的java代码就可以添加新方法,而无需调整javac/JVM.然而,这也是一个非常难的特性,因为数组总是特别用Java来处理,据我所知,它是否已经实现以及何时实现.