小编典典

在 Java 中迭代列表的方法

all

作为 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

    // ...
}

注意:正如@a​​marseillan 指出的那样,这种形式对于迭代Lists
来说是一个糟糕的选择,因为该get方法的实际实现可能不如使用Iterator. 例如,LinkedList实现必须遍历 i
之前的所有元素以获得第 i 个元素。

在上面的示例中,List实现无法“保存其位置”以使未来的迭代更高效。对于
aArrayList这并不重要,因为复杂性/成本get是常数时间(O(1)),而对于 aLinkedList它与列表的大小成正比(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 的 map 方法(参见@i_am_zero 的答案)。)

在 Java 8 中,实现Iterable(例如,所有Lists)的集合类现在有一个forEach方法,可以用来代替上面演示的for
循环语句。

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

如果有的话,还有哪些其他方法?

(顺便说一句,我的兴趣根本不是出于优化性能的愿望;我只是想知道作为开发人员可以使用哪些形式。)


阅读 150

收藏
2022-03-04

共1个答案

小编典典

三种循环形式几乎相同。增强的for循环:

for (E element : list) {
    . . .
}

根据Java
语言规范
,在效果上与显式使用带有传统循环的迭代器
相同。for在第三种情况下,您只能通过删除当前元素来修改列表内容,并且只有通过remove迭代器本身的方法才能这样做。使用基于索引的迭代,您可以以任何方式自由修改列表。但是,添加或删除当前索引之前的元素可能会导致循环跳过元素或多次处理相同的元素;进行此类更改时,您需要正确调整循环索引。

在所有情况下,element都是对实际列表元素的引用。没有任何迭代方法会复制列表中的任何内容。的内部状态的变化element总是会在列表中相应元素的内部状态中看到。

本质上,迭代列表只有两种方法:使用索引或使用迭代器。增强的 for 循环只是 Java 5
中引入的一种语法快捷方式,以避免显式定义迭代器的乏味。对于这两种风格,您可以使用for,whiledo while块提出本质上微不足道的变化,但它们都归结为同一件事(或者,更确切地说,两件事)。

编辑:正如@iX3 在评论中指出的那样,您可以ListIterator在迭代时使用 a
来设置列表的当前元素。您需要使用List#listIterator()而不是List#iterator()初始化循环变量(显然,必须将其声明为
aListIterator而不是 a Iterator)。

2022-03-04