【问题标题】:In Java 8, why were Arrays not given the forEach method of Iterable?在 Java 8 中,为什么没有给数组提供 Iterable 的 forEach 方法?
【发布时间】:2016-06-01 19:16:50
【问题描述】:

我一定是错过了什么。

在 Java 5 中,引入了 "for-each loop" statement (also called the enhanced for loop)。似乎它主要是为了迭代Collections而引入的。任何实现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) 并不自然。

【问题讨论】:

  • 可能是因为它不值得——集合比数组更容易使用,当您需要使用数组时,asList(myArray) 是一个非常简单的解决方法。
  • 你在开玩笑吗?他们甚至没有自己的toString()
  • @Bohemian 是什么意思?
  • 我们怎么知道答案会缺少参考而不允许答案?
  • 可以将数组转换为 Java 8 流:Arrays.stream(a).forEach(...);。我不知道为什么这个问题被关闭了,可能会有很好的答案。

标签: java arrays foreach java-8 javac


【解决方案1】:

假设将特殊代码添加到java编译器中来处理forEach。 然后可以提出许多类似的问题。 为什么我们不能写myArray.fill(0)?还是myArray.copyOfRange(from, to)?还是myArray.sort()myArray.binarySearch()? myArray.stream()?几乎Arrays 中的每个静态方法 接口可以转化为“数组类”对应的方法。 JDK 开发人员为什么要停下来 myArray.forEach()?但是请注意,每个这样的方法不仅必须添加到类库规范中, 但是进入更稳定和保守的Java语言规范。这也意味着,不仅 此类方法的实现将成为规范的一部分,但也将成为 java.util.function.Consumer 之类的类 应该在 JLS 中明确提及(这是提议的forEach 方法的参数)。 另请注意,需要将新的消费者添加到 标准库,如FloatConsumerByteConsumer 等,用于对应的数组类型。 目前 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 中被特殊对待,并且, 据我所知,它是否会实施以及何时实施尚不清楚。

【讨论】:

  • 感谢 JDK 开发人员的观点!数组上缺少其他 Collection 方法并不困扰我,因为它们只有在查看 Javadoc 的集合时才可见(甚至是java.lang.Object),并且想知道为什么不将它们“带入”数组(某种逆向工程) .增强型 for 循环和 forEach, OTOH 是数组可用性的自然候选者,因此是问题所在。
  • @KedarMhaswade,虽然我是 JDK 开发人员,但我实际上是这里的新手,所以我更喜欢外人,我的回答不应该被视为官方的。可能 Stuart Marks 会在 AMA 会议期间对此发表评论(实际上我已经通过 virtualJUG 网站找到了这个问题)。
  • @KedarMhaswade 不幸的是,这个问题没有在 VirtualJUG AMA 中得到讨论。我还通过您在 VirtualJUG 论坛上的帖子发现了这个问题。
【解决方案2】:

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

  • implements Cloneable, Serializable
  • public final int length
  • public T[] clone() 其中T 是数组的组件类型

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

Object[].class.getInterfaces()

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

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

当在 Java SE 5 中添加增强型 for 循环(“for-each”)时,它支持右侧表达式的两种不同情况:Iterable 或数组类型 (JLS 14.14.2) .原因是 Iterable 实例和数组被增强的 for 语句处理完全不同。 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 运行到数组的长度是非常惯用的,每次递增一。数组上的增强型 for 循环正是这样做的。如果使用Iterable 协议实现数组上的增强for循环,我想大多数人会惊讶地发现循环数组涉及初始方法调用和内存分配(创建Iterator),然后每次循环迭代两次方法调用。

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

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

不过,如果 Java 语言和 JVM 中用于数组的特殊情况被 real 构造替换(同时修复一堆其他与数组相关的问题,例如糟糕的处理 2+ 维数组、2^31 长度限制等)。这是 John Rose 领导的“Arrays 2.0”调查的主题。请参阅 John 在 JVMLS 2012 上的演讲 (video, slides)。与此讨论相关的想法包括为数组引入一个实际接口,以允许库插入元素访问,以支持切片和复制等其他操作。

请注意,所有这些都是调查和未来的工作。在撰写本文时 (2016-02-23),Java 路线图中没有任何来自这些数组增强功能的内容。

【讨论】:

  • 感谢您或多或少的官方观点、参考资料,最重要的是,感谢您抽出宝贵时间将其全部写完!尽管我的观察主要是关于使 for-each 和 forEach 的行为与数组的(天真)预期一样(而不是让数组实现 Iterable),但您的回答确实提供了一些有价值的见解。
  • 哇,天才博伊!
猜你喜欢
  • 2023-03-30
  • 2012-01-18
  • 2014-05-31
  • 2016-10-31
  • 2018-02-24
  • 2011-02-15
  • 2010-11-12
相关资源
最近更新 更多