【问题标题】:Does the Internal Iterator use the hasNext() and next() to iterate in Java?内部迭代器是否使用 hasNext() 和 next() 在 Java 中进行迭代?
【发布时间】:2020-12-15 07:10:22
【问题描述】:

我正在学习内部迭代器,并且我了解内部迭代器在后台管理迭代:

   public void internalIterator(){
      List<String> namesList = Arrays.asList("Tom", "Dick", "Harry");
      namesList.forEach(name -> System.out.println(name));
   }

但我认为增强的 for 循环做同样的事情:

 public void enhancedForLoop(){
  List<String> namesList = Arrays.asList("Tom", "Dick", "Harry");
  for(String name : namesList){
    System.out.println(name);
  }
 }

我知道增强的 for 循环在后台使用 hasNext() 和 next() 方法。并且增强的 for 循环是一个外部迭代器。那么为什么 forEach() 方法是一个内部迭代器呢? forEach() 方法不是在后台使用 hasNext() 和 next() 方法吗?如何以不同于增强的 for 循环的方式在 backgorund 中管理迭代?使用 forEach() 的迭代是否比使用增强的 for 循环更快?任何反馈将不胜感激?

【问题讨论】:

  • forEach 的实现取决于集合的类型。例如,ArrayList 使用标准索引循环。 LinkedList 继承了 Iterable 的默认实现,它使用了增强的 for 循环。增强的 for 循环使用迭代器来遍历集合,所以答案是:这取决于你使用的集合。
  • @Dioxin 你有“ArrayList 使用标准索引循环”的来源吗? for-each 循环的最终操作数必须是 Iterable,这意味着始终使用 Iterable.iterator()。
  • @VGR 关于“for-each 循环的最终操作数必须是 Iterable”,您想说什么?哪个最终操作数具有 for-each 循环?你有链接吗?谢谢!
  • @VGR 可以查看源代码。这是 OpenJDK 的ArrayList#forEach 的链接:github.com/openjdk/jdk/blob/master/src/java.base/share/classes/…
  • @VGR iterator() 并不总是使用。引入forEach 背后的目标之一是允许特定于实现的迭代。 ArrayList 仍然是 Iterable。但是开发人员认为使用索引循环而不是列表的迭代器更为优化。 ArrayDeque 还提供了它自己的 forEach 实现,它不使用 iterator()。至于增强循环,Iterable 只是一项要求(JLS 14.14.2);在增强循环中使用数组时,它会转换为索引循环。

标签: java loops foreach iterator


【解决方案1】:

我不知道您从哪里得到“增强的 for 循环是一个外部迭代器”,但 Iterable.forEach 与您的第二个示例执行相同的操作(可以被覆盖,但我看不出有任何理由) .

default void forEach(Consumer<? super T> action) {
    Objects.requireNonNull(action);
    for (T t : this) {
        action.accept(t);
    }
}

【讨论】:

  • 增强的 for 循环是一个外部迭代器,而新的 forEach 方法是一个内部迭代器:baeldung.com/foreach-java
  • 好的,我还没有听说过这个定义,但是好的。顺便提一句。我发现了另一个相关的问题,但要注意那里的答案有些固执己见(仍然对差异进行了很好的讨论,尽管它混合了很多半相关的 Stream API 讨论):stackoverflow.com/questions/16635398/…
  • @elvis 看起来作者正在尝试哲学化。我认为他们正试图断言一些巨大的差异,因为一个是声明性的,另一个是命令式的。我不知道当迭代器的整个存在被隐藏时,他们如何将增强的 for 循环称为“外部”迭代器。我会忽略那篇文章,对我来说似乎很愚蠢。
  • @Michael 这些术语早在撰写该文章之前就已使用。这是 2008 年有关 Stack Overflow 的示例:stackoverflow.com/questions/224648/…
  • @Dioxin 这并不意味着增强的 for 循环是一个外部迭代器。文章说,在提到增强的 for 循环时,“这些操作的复杂性对程序员来说是隐藏的,但它仍然存在”。嗯,forEach 也是如此,正如您从上面的 sn-p 中看到的那样。
【解决方案2】:

另见Iterable接口上forEach的定义。 两种构造都使用同一接口的不同方法。

从概念上讲,区别在于,在“增强的 for 循环”中,实现 Iterable 的类仅创建 Iterator,而循环构造负责推进它(另请参阅 this related question)。

当调用forEach时,该类控制整个迭代过程,并且可以选择最适合其底层数据结构的任何内容。例如,它可以避免创建 Iterator 对象,而是使用一些内部数组索引或类似的。

除此之外,它们应该是等价的。在大多数情况下,我预计性能不会有任何差异。

请参阅 this excellent answer 了解您可能想要使用 forEach 的其他一些原因,例如迭代同步集合时额外的一致性保证,this one 提到它不提供的东西,例如流控制(例如使用break 短路)或支持检查异常。

【讨论】:

  • 感谢您的回复!增强的 for 循环总是使用迭代器还是可以使用索引计数器?我正在查看您发布的链接,最佳答案是:“如果 for (:) 习惯用法的右侧是数组而不是 Iterable 对象,则内部代码使用 int 索引计数器并检查array.length 代替"
  • 是的,这两种情况在JLS 14.14.2. The enhanced for statement 中明确涵盖。数组使用索引,Iterables 使用iterator(),不支持其他类型。
  • 因此,“增强的 for 循环”对数组使用索引,对 Iterables 使用 iterator()。而且 forEach() 方法不适用于数组,它仅适用于 Iterables,但对于 Iterables,它可以使用 iterator() 或索引,具体取决于它使用的集合。对吗?
【解决方案3】:

区别在于 API 的用户必须做什么。


区别

外部

使用iterator() 时,管理遍历是用户的工作(您的工作):何时调用hasNext() 以及何时调用next()。迭代由 API 的用户在外部处理。用户既遍历元素又消费元素。

内部

使用forEach 时,管理遍历是API 的工作。迭代由内部处理,而不是由用户处理;用户只消费元素。


结论

hasNext()next() 是否被调用无关紧要。重要的是 调用 hasNext()next() - 谁处理迭代,谁负责迭代器。

forEach 是内部的,因为用户无法控制元素的迭代方式。 API 处理它;迭代器是内部的。

iterator() 是外部的,因为用户必须定义​​迭代将如何发生。 API 将迭代器传递给您,这就是 API 所做的一切。迭代器是外部的。

即使forEach 在某些情况下使用iterator(),该迭代器仍然在您使用的API 内部(List)。打电话给forEach 的人仍然只担心消耗元素。用户无法控制元素的遍历方式,因此forEach 使用什么并不重要。


关于何时使用策略的建议

当您需要最基本的顺序迭代时,您会使用增强循环。

当您需要更复杂的迭代时,您可以使用iterator()

当您只担心消耗元素并且不介意 API 决定如何遍历集合时,您可以使用 forEach

【讨论】:

  • 所以当你使用 iterator() 时是外部的,而当你使用 forEach 时是外部的。增强的 for 循环怎么样?
  • @elvis forEach 是内部的。使用增强的 for 循环与自己使用 iterator() 没有什么不同。增强的 for 循环确实可以让您不必编写 for (Iterator&lt;String&gt; i = namesList.iterator(); i.hasNext(); ) { String name = i.next(); ... }(如果使用 Iterable)。但它的工作方式与您自己编写所有代码完全相同。这就是为什么它被认为是“语法糖”。因此,使用增强的 for 循环将使用外部迭代器。
  • 对不起,我想说 forEach 是内部的。
猜你喜欢
  • 2020-12-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-29
  • 2015-07-20
  • 2015-06-25
  • 2023-03-06
  • 2010-12-30
相关资源
最近更新 更多