我必须在这里遗漏一些东西。
在Java 5中,引入了"for-each loop" statement (also called the enhanced for loop)。它似乎主要是为了迭代集合而引入的。任何实现Iterable
接口的集合(或容器)类都可以使用“for-each循环”进行迭代。也许出于历史原因,Java阵列没有实现Iterable接口。但由于数组无处不在,javac
会接受在数组上使用for-each循环(生成相当于传统for循环的字节码)。
在Java 8中,forEach
method作为默认方法添加到Iterable
接口。这使得将lambda表达式传递给集合(迭代时)成为可能(例如list.forEach(System.out::println)
)。但同样,阵列不喜欢这种治疗方法。 (我知道有解决方法)。
有没有技术上的原因为什么javac无法增强以接受forEach中的数组,就像它在增强的for循环中接受它们一样?似乎可以在不要求数组实现Iterable
的情况下生成代码。我是天真的吗?
这对于语言的新手来说尤其重要,因为他们的语法很容易,他们更自然地使用数组。切换到列表并使用Arrays.asList(1, 2, 3)
。
答案 0 :(得分:43)
Java语言和JVM中有许多特殊情况。数组有一个API,但它几乎看不到。就像声明数组有:
implements Cloneable, Serializable
public final int length
public T[] clone()
其中T
是数组的组件类型但是,这些声明在任何地方的任何源代码中都不可见。有关说明,请参阅JLS 4.10.3和JLS 10.7。 Cloneable
和Serializable
通过反射可见,并通过调用
Object[].class.getInterfaces()
也许令人惊讶的是,length
字段和clone()
方法不能反映出来。 length
字段根本不是字段;使用它会变成一个特殊的arraylength
字节码。对clone()
的调用会导致实际的虚方法调用,但如果接收者是数组类型,则由JVM专门处理。
值得注意的是,数组类没有实现Iterable
接口。
当在Java SE 5中添加了增强型for循环(" for-each")时,它支持右侧表达式的两种不同情况:Iterable
或者数组类型(JLS 14.14.2)。原因是增强型for语句完全不同地处理Iterable
个实例和数组。 JLS的那一部分提供了全面的处理,但更简单地说,情况如下。
对于Iterable<T> iterable
,代码
for (T t : iterable) {
<loop body>
}
是
的语法糖for (Iterator<T> iterator = iterable.iterator(); iterator.hasNext(); ) {
t = iterator.next();
<loop body>
}
对于数组T[]
,代码
for (T t : array) {
<loop body>
}
是
的语法糖int len = array.length;
for (int i = 0; i < len; i++) {
t = array[i];
<loop body>
}
现在,为什么这样做?数组肯定可以实现Iterable
,因为它们已经实现了其他接口。编译器也可以合成由数组支持的Iterator
实现。 (有先例。编译器已经合成了自动添加到每个values()
类的静态valueOf()
和enum
方法,如JLS 8.9.3中所述。)
但是数组是一个非常低级的构造,并且通过int
值访问数组预计是非常便宜的操作。从0
运行循环索引到数组的长度是非常惯用的,每次递增1。数组上的增强for循环就是这样。如果使用Iterable
协议实现了数组上的增强for循环,我想大多数人会发现循环数组涉及初始方法调用和内存分配(创建{{1})会让人不高兴。 }),然后每循环迭代两次方法调用。
因此,当在Java 8中将默认方法添加到Iterator
时,这根本不会影响数组。
正如其他人所说,如果您有Iterable
,int
,long
或参考类型的数组,则可以将其转换为流使用其中一个double
来电。这样就可以访问Arrays.stream()
,map()
,filter()
等
但是,如果Java语言中的特殊情况和数组的JVM被真正的结构替换(以及修复一些其他与数组相关的问题,例如差错),那将会很不错处理2维阵列,2 ^ 31长度限制,等等。这是&#34; Arrays 2.0&#34;的主题。调查由约翰罗斯领导。请参阅John在JVMLS 2012上的演讲(video,slides)。与此讨论相关的想法包括引入数组的实际接口,允许库插入元素访问,支持切片和复制等其他操作。
请注意,所有这些都是调查和未来的工作。在撰写本文时(2016-02-23),在任何版本的Java路线图中都没有提供这些数组增强功能。
答案 1 :(得分:7)
假设特殊代码将被添加到java编译器中以处理forEach
。
然后可以提出许多类似的问题。
为什么我们不能写myArray.fill(0)
?还是myArray.copyOfRange(from, to)
?还是myArray.sort()
?
myArray.binarySearch()
? myArray.stream()
?实际上Arrays
中的每个静态方法
接口可以转换为&#34;数组类的相应方法&#34;。为什么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 initiative。 我们的想法是为数组引入一些超类,这些数组将位于类库中,所以 只需编写普通的java代码而不调整javac / JVM就可以添加新的方法。然而, 这也是一个非常难的功能,因为数组总是在Java中专门处理,并且, 据我所知,它是否会被实施以及何时实施。