这些称为 for-each 循环,可以应用于数组和集合。格式:
for ([type] [var-name] : [array or iterable value]) {
...
}
要使用 for-each 循环,您只需要满足以下两个条件之一:
- 迭代值是一个数组,或者
- 迭代值为
Iterable
.
我们这样做的最大原因是因为List
它不是唯一Iterable
可以在循环中使用的,而是唯一可以被索引的。这使您可以进行通用迭代,而不是将自己锁定在固定的索引列表格式中。
例如,HashSet
没有排序,但我仍然可以在 for-each 循环中对其进行迭代:
HashSet<String> strs = new HashSet<String>();
for (String str : strs) { ... }
我也可以有一个方法采取 anIterable
而不是 a List
:
public static void printStrings(Iterable<String> strs) {
for (String str : strs) {
System.out.println(str);
}
}
如果不先将其复制到其他东西(例如数组或List
.
使用 for-each 循环的另一个重要原因是它编译为使用Iterator
列表外(通过Iterable.iterator()
),这允许快速失败迭代。这会导致类似ConcurrentModificationException
s 的信息告诉我们,在我们对其进行迭代时,该集合已被修改。例如,这将总是抛出一个ConcurrentModificationException
:
List<String> strs = ... ;
for (String str : strs) {
strs.remove(str);
}
但这不会:
List<String> strs = ... ;
for (int i = 0; i < strs.length(); i++) {
strs.remove(str);
}
这对多线程应用程序很有用,因为您应该在访问列表之前锁定列表,因为大多数List
实现都不是为线程安全而设计的,而且如果没有外部同步,无论是使用索引还是使用Iterator
. 如果一个线程对其进行迭代,而另一个线程对其进行了更改,则可以(不能保证,但可以)抛出一个ConcurrentModificationException
,它告诉您该列表未正确锁定。被索引的永远不会给你这种信息。相反,它只会表现出奇怪的行为,比如跳过元素。