作为 Java 语言的新手,我试图让自己熟悉可能遍历列表(或其他集合)的所有方式(或至少是非病态的方式)以及每种方式的优缺点。
给定一个 List<E> list
对象,我知道以下遍历所有元素的方法:
基本 for 循环(当然,也有等效的 while
/ do while
循环)
// Not recommended (see below)!
for (int i = 0; i < list.size(); i++) {
E element = list.get(i);
// 1 - can call methods of element
// 2 - can use 'i' to make index-based calls to methods of list
// ...
}
注意:正如@amarseillan 所指出的,这种形式对于遍历 List
是一个糟糕的选择,因为 get
方法的实际实现可能不如使用 Iterator
。例如, LinkedList
实现必须遍历 i 之前的所有元素以获取第 i 个元素。
在上面的示例中, List
实现无法“保存其位置”以提高未来迭代的效率。对于 ArrayList
这并不重要,因为 get
的复杂性/成本是常数时间 (O(1)) 而对于 LinkedList
与列表的大小成正比 (O(n))。
有关内置 Collections
实现的计算复杂性的更多信息,请查看 此问题。
增强 的 for 循环( 在这个问题中有 很好的解释)
for (E element : list) {
// 1 - can call methods of element
// ...
}
迭代器
for (Iterator<E> iter = list.iterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// ...
}
列表迭代器
for (ListIterator<E> iter = list.listIterator(); iter.hasNext(); ) {
E element = iter.next();
// 1 - can call methods of element
// 2 - can use iter.remove() to remove the current element from the list
// 3 - can use iter.add(...) to insert a new element into the list
// between element and iter->next()
// 4 - can use iter.set(...) to replace the current element
// ...
}
函数式Java
list.stream().map(e -> e + 1); // Can apply a transformation function for e
Iterable.forEach , Stream.forEach , …
(来自 Java 8 的 Stream API 的映射方法(参见@i_am_zero 的回答)。)
在实现 Iterable
的Java 8集合类中(例如,所有 List
s)现在有一个 forEach
方法,而不是 循环语句 上面证明了。 (这是 另一个提供很好比较的问题。)
Arrays.asList(1,2,3,4).forEach(System.out::println);
// 1 - can call methods of an element
// 2 - would need reference to containing object to remove an item
// (TODO: someone please confirm / deny this)
// 3 - functionally separates iteration from the action
// being performed with each item.
Arrays.asList(1,2,3,4).stream().forEach(System.out::println);
// Same capabilities as above plus potentially greater
// utilization of parallelism
// (caution: consequently, order of execution is not guaranteed,
// see [Stream.forEachOrdered][stream-foreach-ordered] for more
// information about this).
如果有的话,还有哪些其他方法?
(顺便说一句,我的兴趣根本不是出于 优化性能 的愿望;我只是想知道作为开发人员可以使用哪些形式。)
原文由 jacobq 发布,翻译遵循 CC BY-SA 4.0 许可协议
这三种循环形式几乎相同。增强的
for
循环:根据 Java Language Specification ,在效果上 等同 于在传统
for
循环中显式使用迭代器。在第三种情况下,您只能通过删除当前元素来修改列表内容,然后,只有通过迭代器本身的remove
方法才能完成。使用基于索引的迭代,您可以自由地以任何方式修改列表。但是,添加或删除当前索引之前的元素可能会导致循环跳过元素或多次处理同一元素;进行此类更改时,您需要适当调整循环索引。在所有情况下,
element
是对实际列表元素的引用。没有一种迭代方法会复制列表中的任何内容。对element
的内部状态的更改将始终在列表中相应元素的内部状态中看到。本质上,只有两种方法可以迭代列表:使用索引或使用迭代器。增强的 for 循环只是 Java 5 中引入的一种语法快捷方式,用于避免显式定义迭代器的单调乏味。对于这两种样式,您可以使用
for
、while
或do while
来想出本质上微不足道的变化—相反,两件事)。编辑:正如@iX3 在评论中指出的那样,您可以在迭代时使用
ListIterator
设置列表的当前元素。您需要使用List#listIterator()
而不是List#iterator()
来初始化循环变量(显然,必须将其声明为ListIterator
而不是Iterator
)。