Comparison of for Loops and Iterators – for 循环和迭代器的比较

最后修改: 2023年 12月 28日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.导言

for loops and Iterators, both provide mechanisms to traverse through collections of elements. While both serve the purpose of iterating over collections, they differ in their syntax, functionality, and applicability.

for 循环Iterators 都提供了遍历元素集合的机制。虽然两者都能达到遍历集合的目的,但它们在语法、功能和适用性上有所不同。

In this tutorial, we’ll explore a detailed comparison between for loops and iterators, highlighting their key distinctions in several aspects.

在本教程中,我们将详细比较 for 循环和 iterators 循环,突出它们在几个方面的主要区别。

We’ll use the following list of strings to demonstrate:

我们将使用下面的字符串列表进行演示:

List<String> names = Arrays.asList("Alice", "Bob", "Charlie");

2. Forward Traversal

2.前向遍历

In this section, we’ll explore the forward traversal methods for both for loops and iterators.

在本节中,我们将探讨 for 循环和 iterators 的正向遍历方法。

2.1. With for Loops

2.1.使用 for 循环

Traditional for loops in Java are designed for forward iteration. They start from an initial index and move toward the end of the collection, processing each element in sequential order.

Java 中传统的 for 循环是为向前迭代而设计的。它们从初始索引开始,向集合的末尾移动,按顺序处理每个元素。

Let’s iterate forward using a for loop:

让我们使用 for 循环向前遍历:

StringBuilder stringBuilder = new StringBuilder(); 

for (int i = 0; i < names.size(); i++) { 
    stringBuilder.append(names.get(i)); 
} 

assertEquals("AliceBobCharlie", stringBuilder.toString());

2.2. With Iterators

2.2.使用 Iterators

Iterators, by default, offer forward-only traversal. The hasNext() method checks for the existence of the next element, and the next() method moves the iterator to the next position in the collection:

迭代器默认只提供向前遍历。hasNext() 方法会检查下一个元素是否存在,而 next() 方法会将迭代器移动到集合中的下一个位置:

StringBuilder stringBuilder = new StringBuilder();

Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
    stringBuilder.append(iterator.next());
}

assertEquals("AliceBobCharlie", stringBuilder.toString());

3. Backward Traversal

3.逆向遍历

In this section, we’ll explore the backward traversal methods for both for loops and iterators.

在本节中,我们将探讨 for 循环和 iterators 的反向遍历方法。

3.1. With for Loops

3.1.使用 for 循环

While it’s possible to simulate backward traversal by manipulating the for loop variable, it isn’t as straightforward as forward iteration. Let’s iterate backward using a for loop:

虽然可以通过操作 for 循环变量来模拟逆向遍历,但它并不像正向遍历那样简单。让我们使用 for 循环进行反向遍历:

StringBuilder stringBuilder = new StringBuilder();

for (int i = names.size() - 1; i >= 0; i--) {
    stringBuilder.append(names.get(i));
}
assertEquals("CharlieBobAlice", stringBuilder.toString());

3.2. With Iterators

3.2.使用 Iterators

However,  if a collection implements the List interface and provides a ListIterator, we can achieve backward iteration using the hasPrevious() and previous() methods:

但是,如果集合实现了 List 接口并提供了 ListIterator 方法,我们就可以使用 hasPrevious()previous() 方法实现后向迭代:

StringBuilder stringBuilder = new StringBuilder();

ListIterator<String> listIterator = names.listIterator(names.size());
while (listIterator.hasPrevious()) {
    stringBuilder.append(listIterator.previous());
}

assertEquals("CharlieBobAlice", stringBuilder.toString());

4. Removal of Elements

4.拆除元件

In this section, we’ll explore the remove methods in both for loops and iterators.

在本节中,我们将探讨 for 循环和 iterators 中的移除方法。

4.1. With for Loops

4.1.使用 for 循环

for loops aren’t directly compatible with removing elements from the collection being traversed. Modifying the collection during a for loop iteration can lead to unpredictable behavior as the size of the collection is modified. This often results in ConcurrentModificationException or incorrect indices.

for 循环与从遍历的集合中删除元素并不直接兼容。for 循环迭代期间修改集合可能会导致不可预测的行为,因为集合的大小会被修改。这通常会导致 ConcurrentModificationException 或不正确的索引。

Let’s test out the remove() method during looping:

让我们测试一下循环过程中的 remove() 方法:

assertThrows(ConcurrentModificationException.class, () -> {
    for (String name : names) {
        names.remove("Bob");
    }
});

4.2. With Iterators

4.2.使用 Iterators

Iterators, on the other hand, provide a safe and reliable way to remove elements during iteration using the remove() method. Iterator internally maintains a cursor or a position within the collection. When we call remove(), it knows exactly which element to remove based on its internal state. This prevents concurrent modification issues and ensures the integrity of the iteration process.

另一方面,迭代器提供了一种安全可靠的方法,可在迭代过程中使用 remove() 方法删除元素。迭代器在内部维护一个光标或集合中的一个位置。当我们调用remove()时,它会根据内部状态准确地知道要删除哪个元素。这可防止并发修改问题,并确保迭代过程的完整性。

Let’s test out the remove() method with Iterator:

让我们用 Iterator 测试 remove() 方法:

Iterator<String> iterator = names.iterator();

while (iterator.hasNext()) {
    String name = iterator.next();
    if (name.equals("Bob")) {
        iterator.remove();
    }
}

List<String> expected = Arrays.asList("Alice", "Charlie");
assertIterableEquals(expected, names);

5. Flexibility

5.灵活性

In this section, we’ll explore the flexibility to alter elements during iteration in both for loops and iterators.

在本节中,我们将探讨在 for 循环和 iterators 中迭代期间更改元素的灵活性。

5.1. With for Loops

5.1.使用 for 循环

for loops provide direct access to the elements of a collection based on their indices. This offers flexibility in terms of modification and access, as we have explicit control over the index and can easily perform insertions and modification operations:

for循环可根据索引直接访问集合中的元素。这为修改和访问提供了灵活性,因为我们可以对索引进行显式控制,并轻松执行插入和修改操作:

for (int i = 0; i < names.size(); i++) {
    names.set(i, names.get(i).toLowerCase());
}

List<String> expected = Arrays.asList("alice","bob", "charlie");
assertIterableEquals(expected, names);

5.2. With Iterators

5.2.使用 Iterators

Iterators, while excellent for traversal and removal, don’t provide direct access to index-based operations. The Iterator interface focuses on forward-only traversal and removal, limiting the ability to directly insert or modify elements. If we need to add or modify elements using Iterator, we may want to consider ListIterator.

迭代器虽然在遍历和移除方面表现出色,但却不能直接访问基于索引的操作。迭代器接口侧重于仅向前遍历和移除,从而限制了直接插入或修改元素的能力。如果我们需要使用 Iterator 添加或修改元素,我们可能需要考虑使用 ListIterator

6. Error-proneness

6.容易出错

for loops are more prone to errors due to their reliance on index-based access. Incorrect index values or modifications to the collection during iteration can lead to various exceptions and unexpected behavior. For example, for loop can lead to IndexOutOfBoundException if the index value is outside the bounds of the collection. This can happen if the index variable isn’t properly initialized or if the collection size is modified during iteration.

for 循环由于依赖于基于索引的访问,因此更容易出错。在迭代过程中,不正确的索引值或对集合的修改可能会导致各种异常和意外行为。例如,如果索引值超出了集合的范围,for 循环可能会导致 IndexOutOfBoundException 异常。如果索引变量没有正确初始化,或者在迭代过程中修改了集合大小,就会发生这种情况。

On the other hand, Iterator enforces hasNext() checks before accessing elements, preventing null pointer exceptions. This ensures that the Iterator points to a valid element before attempting to access it.

另一方面,Iterator 会在访问元素前执行 hasNext() 检查,从而防止出现空指针异常。这可确保 Iterator 在尝试访问元素之前指向有效元素。

7. Code Readability

7.代码可读性

for loops are generally considered more readable and concise for simple iterations over collections due to their straightforward syntax. The loop structure clearly conveys the iteration logic, with the index variable explicitly indicating the current position in the collection. This makes it easy to understand the code and follow the flow of the iteration.

for循环由于其语法简单明了,通常被认为对于集合上的简单迭代更具有可读性和简洁性。循环结构清晰地传达了迭代逻辑,索引变量明确指示了当前在集合中的位置。这使得理解代码和跟踪迭代流程变得容易。

While Iterator offers benefits for complex scenarios, it can introduce some readability challenges for simple iterations. Iterators require method calls like hasNext() or next() to iterate through the collection. These method calls can introduce additional complexity and make the iteration logic less clear compared to the concise syntax of a for loop.

虽然迭代器为复杂的应用场景带来了好处,但它也为简单的迭代带来了一些可读性方面的挑战Iterators 需要调用 hasNext()next() 等方法来遍历集合。与 for 循环的简洁语法相比,这些方法调用可能会带来额外的复杂性,并使迭代逻辑变得不那么清晰。

8. Choosing Between Iterators and for Loops

8.在 Iterators 和 for 循环之间做出选择

In summary, for loops are suitable for simple iteration, especially when direct access to indices is beneficial.

总之,for循环适用于简单的迭代,尤其是当直接访问索引是有益的

Iterators, on the other hand, are powerful when dealing with safe removal, forward-only traversal, and when working with various collection types.

<另一方面,迭代器在处理安全移除、仅向前遍历以及处理各种集合类型时非常强大。

The following table shows the main differences between the for loop and Iterator:

下表显示了 for 循环和 Iterator 之间的主要区别:

Feature for Loop Iterator
Traversal Direction Forward and backward using indexing Forward (default), bidirectional with ListIterator
Element Removal Not directly compatible, can lead to errors Safe and reliable using remove() method
Flexibility – Insert, Access, Modify Direct index-based access Limited to forward-only traversal and removal; ListIterator for modification while iterating
Error-proneness More prone to errors due to index-based access and potential modifications Enforce hasNext() checks, reducing null pointer exceptions

9. Conclusion

9.结论

In this article, we discussed the difference between for loops and an Iterators.

在本文中,我们讨论了 for 循环和 Iterators 之间的区别。

for loops provides a straightforward approach for simple forward traversal, while Iterators are powerful when dealing with safe removal and forward-only traversal.

for 循环为简单的正向遍历提供了一种直接的方法,而 Iterators 在处理安全删除和仅正向遍历时功能强大。

As always, the source code for the examples is available over on GitHub.

与往常一样,这些示例的源代码可在 GitHub 上获取。