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

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

问题描述

我一定在这里遗漏了什么.

I must be missing something here.

在 Java 5 中,for-每个循环"语句(也称为增强的 for 循环) 被引入.似乎引入它主要是为了遍历Collections.任何实现 Iterable 接口可以使用for-each 循环"进行迭代.也许由于历史原因,Java 数组没有实现 Iterable 接口.但由于数组无处不在,javac 将接受在数组上使用 for-each 循环(生成相当于传统 for 循环的字节码).

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 8 中,forEach 方法 被添加到 Iterable 接口作为 默认 方法.这使得将 lambda 表达式传递给集合(在迭代时)成为可能(例如 list.forEach(System.out::println)).但同样,阵列不享受这种待遇.(我知道有一些解决方法).

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

javac 不能被增强以接受 forEach 中的数组是否存在技术原因,就像它在增强的 for 循环中接受它们一样?似乎不需要数组实现 Iterable 就可以生成代码.我太天真了吗?

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?

这对于该语言的新手来说尤其重要,因为数组的语法易于使用,所以他们很自然地使用数组.切换到列表并使用 Arrays.asList(1, 2, 3) 几乎是不自然的.

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

推荐答案

在 Java 语言和 JVM 中对于数组有很多特殊情况.数组有一个 API,但它几乎不可见.就好像数组被声明为:

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:

  • 实现可克隆、可序列化
  • public final int 长度
  • public T[] clone() 其中 T 是数组的组件类型
  • implements Cloneable, Serializable
  • public final int length
  • public T[] clone() where T is the array's component type

然而,这些声明在任何源代码中都不可见.请参阅 JLS 4.10.3JLS 10.7为解释.CloneableSerializable 通过反射可见,并通过调用

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

也许令人惊讶的是,length 字段和 clone() 方法在反射中是不可见的.length 字段根本不是一个字段;使用它会变成一个特殊的 arraylength 字节码.对 clone() 的调用会导致实际的虚方法调用,但如果接收者是数组类型,则由 JVM 专门处理.

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.

不过,值得注意的是,数组类没有实现 Iterable 接口.

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

在 Java SE 5 中添加增强型 for 循环(for-each")时,它支持右侧表达式的两种不同情况:Iterable 或数组类型(JLS 14.14.2).原因是 Iterable 实例和数组被 Enhanced-for 语句处理的完全不同.JLS的那部分给出了完整的处理,但更简单地说,情况如下.

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.

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

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

但是数组是一种非常低级的构造,通过 int 值访问数组预计是非常便宜的操作.运行从 0 到数组长度的循环索引,每次递增 1,这是非常惯用的.数组上的增强 for 循环正是这样做的.如果使用 Iterable 协议实现数组上的增强 for 循环,我想大多数人会惊讶地发现循环数组涉及初始方法调用和内存分配(创建 Iterablecode>Iterator),然后是每次循环迭代两次方法调用.

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.

因此,在 Java 8 中将默认方法添加到 Iterable 时,这根本不会影响数组.

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

正如其他人所指出的,如果您有一个 intlongdouble 或引用类型的数组,则可以将使用 Arrays.stream() 调用之一将其转换为流.这提供了对 map()filter()forEach() 等的访问.

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.

不过,如果 Java 语言和 JVM 中用于数组的特殊情况被替换为真实 构造(同时修复一堆其他与数组相关的问题,例如差2+ 维数组的处理、2^31 长度限制等).这是由 John Rose 领导的Arrays 2.0"调查的主题.参见 John 在 JVMLS 2012 上的演讲(视频幻灯片).与本次讨论相关的想法包括引入数组的实际接口、允许库插入元素访问、支持额外的操作,如切片和复制等.

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.

请注意,所有这些都是调查和未来的工作.在撰写本文时 (2016 年 2 月 23 日),任何版本的 Java 路线图中都未承诺这些数组增强功能.

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 中,为什么没有为数组提供 Iterable 的 forEach 方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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