当前位置: 首页 > 面试题库 >

在Java 8中,为什么未为Array提供Iterable的forEach方法?

梁英喆
2023-03-14
问题内容

我一定在这里想念什么。

在Java 5中,引入了“ for-
each循环”语句(也称为增强的for循环)
。似乎引入它主要是为了遍历
Collections
。任何实现该Iterable接口的集合(或容器)类都可以使用“
for-
each循环”进行迭代。也许出于历史原因,Java数组未实现Iterable接口。但是由于数组是普遍存在的,因此javac可以接受在数组上使用for-
each循环(生成与传统的for循环等效的字节码)。

在Java
8中,该forEach方法已Iterable作为 默认
方法添加到接口中。这样就可以将lambda表达式传递给集合(同时进行迭代)(例如list.forEach(System.out::println))。但是同样,数组不享受这种待遇。(我知道有解决方法)。

是否有技术上的原因导致无法像在增强型for循环中接受Javac一样无法对其进行增强以接受forEach中的数组?看起来,不需要数组实现就可以生成代码Iterable。我天真吗?

这对于该语言的新手来说尤其重要,后者由于语法上的易用性而自然使用数组。切换到Lists并使用并不自然Arrays.asList(1, 2, 3)


问题答案:

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循环(“ for-each”)时,它为右侧表达式支持两种不同的情况:an Iterable或数组类型(JLS
14.14.2)。原因是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数组的长度开始,每次递增一个,这是很习惯的。数组上的Enhanced-
for循环正是这样做的。如果使用Iterable协议在数组上进行增强的循环,我想大多数人会很不惊讶地发现数组上的循环涉及初始方法调用和内存分配(创建Iterator),然后每个循环进行两次方法调用迭代。

因此,当IterableJava 8 中添加了默认方法时,这根本不会影响数组。

正如其他人所指出的,如果你有一个数组intlongdouble,或引用类型,它可能把它变成使用的一个流Arrays.stream()调用。这提供了访问map()filter()forEach(),等。

不过,如果将Java语言和JVM数组中的特殊情况替换为 实际 结构(以及解决其他与数组相关的问题,例如对2+维数组的处理不善,则2 ^
31长度限制,依此类推)。这是John Rose领导的“ Arrays 2.0”调查的主题。请参阅JVMLS
2012上John的演讲(视频,幻灯片)。与该讨论相关的思想包括引入数组的实际接口,以允许库插入元素访问,以支持其他操作,例如切片和复制等。

请注意,所有这些都是调查和将来的工作。截至撰写本文时(2016-02-23),任何发行版的Java路线图中都没有落实这些数组增强功能



 类似资料:
  • 本质上,它包装了,确保了最大容量,并提供了一些其他有用的特性。更好的方法是直接将其实现为。 现在,为了方便起见,我认为最好实现,这样,如果您想要循环它,就可以使用增强的for-loops。(我的类还提供了方法,因此我认为是合理的。) 接口提供以下功能(省略了javadoc): null

  • 当我看到Iterable接口源时,它看起来像方法和方法不是抽象的。接口如何具有非抽象方法?还是我在这件事上漏掉了什么?请参阅下面的接口源代码。

  • 问题内容: Iterables提供了两种方法 但只有一个 是否存在任何破坏对称性的设计/实现原因? 问题答案: 我认为要点是,没有理由可以使用来完成此操作。Guava尽力使API保持较小,因此没有添加可以/应该以其他方式轻松完成的事情。 另一方面,还没有一种机制可以测试iterable是否为空,如果是,则返回默认值而不是第一个值。因此,。 同样,没有简单的方法来获取最后一个元素,因此和

  • 问题内容: 有人知道为什么JUnit 4提供但不提供方法吗? 它提供了(对应于)和(对应于),因此它们似乎没有包含在内就显得很奇怪。 顺便说一下,我知道JUnit插件提供了我正在寻找的方法。我只是出于好奇而问。 问题答案: 我建议您使用较新的样式断言,该断言可以轻松描述各种否定形式,并在断言失败时自动构建对您的期望和得到的结果的描述: 这三个选项都是等效的,请选择最容易阅读的一个。 要使用方法的简

  • 我很想知道为什么Java的可选不提供类似于流的方法。 接口的method javadoc声明: @apiNote此方法主要用于支持调试,您希望在元素流经管道中的某个点时看到这些元素 这几乎完全描述了我的用例: (返回 方法,上述所有内容都会转换为: 也可以这样做(参见此答案): 并将其与方法一起使用: 但我认为这是一个黑客,而不是的干净用法。 从Java 9开始,可以将< code>Optiona

  • 我最近开始检查新的Java8特性。 我遇到过这个迭代器-它对集合进行迭代。 比如,我希望它从第2个值开始,然后迭代到第2个最后一个值。或者类似的东西--或者其他元素。 我要怎么做?