在Java 8中,为什么数组没有给出forEach方法的Iterable? [英] In Java 8, why were Arrays not given the forEach method of Iterable?

查看:155
本文介绍了在Java 8中,为什么数组没有给出forEach方法的Iterable?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须在这里错过一些东西。

在Java 5中,for-each loop语句(也称为增强for循环)被引入。看来它主要是为了迭代 Collections 而引入的。任何实现 Iterable 接口可以使用for-each循环迭代。也许由于历史原因,Java数组没有实现Iterable接口。但是由于数组是普遍存在的,所以 javac 会接受在数组上使用for-each循环(生成相当于传统for循环的字节码)。
$ b

在Java 8中, Iterable 接口中的.font.Consumer-rel =noreferrer> forEach 作为默认的方法。这使得将lambda表达式传递给集合(同时迭代)成为可能(例如 list.forEach(System.out :: println))。但是,阵列不享受这种治疗。 (我知道有解决方法)。

有没有技术上的原因,为什么javac无法增强接受forEach中的数组,就像它接受增强的for循环?看来代码生成是可能的,而不需要数组实现 Iterable 。我是天真的吗?

对于一个语言的新手而言,这对于自然地使用数组是非常重要的,因为他们的语法简单。切换到列表并使用 Arrays.asList(1,2,3)

在Java语言和数组的JVM中有很多特殊情况。数组有一个API,但几乎看不见。这就好像数组被声明为具有:



但是,这些声明在任何地方的任何源代码中都是不可见的。请参阅 JLS 4.10.3 JLS 10.7 的解释。 Cloneable Serializable 通过反射可见,并通过调用返回

  Object []。class.getInterfaces()



也许令人惊讶的是,长度字段和 clone()方法是不可见的。 长度字段根本不是字段;使用它变成一个特殊的 arraylength 字节码。调用 clone()会导致一个实际的虚方法调用,但是如果接收方是一个数组类型,那么JVM就会专门处理这个方法。
$ b

值得注意的是,数组类没有实现 Iterable 接口。在Java SE 5中增加for-loop(for-each)时,它支持右侧表达式的两种不同情况: Iterable 或数组类型( JLS 14.14.2 )。原因在于 Iterable 实例和数组的处理完全不同于enhanced-for语句。 JLS的这一部分给出了完整的处理,但是更简单地说,情况如下。

对于 Iterable< T> (T t:iterable){
,代码

 体> 



$ b $ p






$ b $ (Iterator< T> iterator = iterable.iterator(); iterator.hasNext();){
t = iterator.next();
<循环体>

对于一个数组 T [] < loop body>

$ p $



$ b $ p






$ b $

  for(int i = 0; i< array.length; i ++){
t = array [i];
<循环体>





$ b现在,为什么这样做?数组当然可以实现 Iterable ,因为它们已经实现了其他接口。编译器也可以合成一个由数组支持的 Iterator 实现。 (有一个先例,编译器已经自动合成了自动的 values() valueOf()添加到每个 enum 类,如 JLS 8.9.3 。)

但是数组是一个非常低级的构造,期望 int 值的操作非常廉价。将循环索引从 0 运行到一个数组的长度,每次增加一个是相当习惯的。数组上增强的for循环就是这样做的。如果使用 Iterable 协议来实现数组上的增强型for循环,我想大多数人会发现循环数组涉及初始方法调用,内存分配(创建 Iterator ),然后在每个循环迭代中调用两个方法。因此,当在Java 8中将默认方法添加到 Iterable 中时,这完全不会影响数组。 / p>

其他人已经注意到,如果你有一个 int long double )或引用类型,可以使用 Arrays.stream() code>来电。这提供了对 map() filter() forEach()等。

如果Java语言和数组的JVM中的特殊情况被 real < (修复一堆其他数组相关的问题,比如2维数组处理不当,2 ^ 31长度限制等等)。这是约翰·罗斯领导的阵列2.0调查的主题。请参阅John在JVMLS 2012上的演讲(视频载玻片的)。与此讨论相关的观点包括引入数组的实际接口,允许库插入元素访问,以支持切片和复制等附加操作。



<请注意,这一切都是调查和未来的工作。截至撰写本文(2016-02-23)为止,任何版本的Java路线图都没有提供这些阵列增强功能。


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).

In Java 8, the forEach method was added to the Iterable interface as a default method. 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).

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?

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).

解决方案

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:

  • implements Cloneable, Serializable
  • public final int length
  • public T[] clone() where T is the array's component type

However, these declarations aren't visible in any source code anywhere. See JLS 4.10.3 and JLS 10.7 for explanations. Cloneable and Serializable are visible via reflection, and are returned by a call to

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.

Notably, though, array classes do not implement the Iterable interface.

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.

For an Iterable<T> iterable, the code

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

for (T t : array) {
    <loop body>
}

is syntactic sugar for

for (int i = 0; i < array.length; 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.)

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.

So when default methods were added to Iterable in Java 8, this didn't affect arrays at all.

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.

It would be nice, though, if the special cases in the Java language and JVM for arrays were replaced by real constructs (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.

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).

这篇关于在Java 8中,为什么数组没有给出forEach方法的Iterable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆