包装迭代器的 Iterable¶
ID: java/iterable-wraps-iterator
Kind: problem
Security severity:
Severity: warning
Precision: very-high
Tags:
- correctness
- reliability
Query suites:
- java-security-and-quality.qls
Java 有两个用于处理迭代的接口,Iterable<T>
和 Iterator<T>
。 Iterable<T>
表示可以遍历的元素序列,而 Iterator<T>
表示正在进行的遍历的状态。例如,Java 标准库中的所有 Collection<T>
类都实现了 Iterable<T>
。将此与传统 for
循环进行比较,该循环递增整数索引并遍历数组元素,则 Iterable<T>
对象对应于数组,而 Iterator<T>
对象对应于索引变量。
通常情况下,Iterable<T>
的实现应支持对它们所表示的元素序列进行多次遍历,但如果底层数据以某种方式使得这样做不合适,则可能存在例外,例如 DirectoryStream<T>
。如果 Iterable<T>
的实现不支持多次迭代,则其 iterator()
方法应在其第二次及后续调用中引发异常。如果此类 Iterable<T>
被多次使用(例如在两个不同的 foreach 循环中),这样做可以更轻松地找到错误。
建议¶
在 Iterable<T>
中编写 iterator()
方法时,务必确保每次调用都会产生一个新的 Iterator<T>
实例,其中包含跟踪迭代所需的所有状态。如果迭代器存储在 Iterable<T>
中,或者以某种方式引用存储在 Iterable<T>
中的迭代状态,则对 iterator()
的后续调用可能会导致仅遍历元素子集的循环,或者根本不起作用。
示例¶
以下示例在每次调用时都会返回相同的迭代器,因此会导致第二个循环立即终止,没有任何效果。
class MySequence implements Iterable<MyElem> {
// ... some reference to data
final Iterator<MyElem> it = data.iterator();
// Wrong: reused iterator
public Iterator<MyElem> iterator() {
return it;
}
}
void useMySequence(MySequence s) {
// do some work by traversing the sequence
for (MyElem e : s) {
// ...
}
// do some more work by traversing it again
for (MyElem e : s) {
// ...
}
}
第二个示例每次都会返回一个新创建的迭代器,但仍然依赖于存储在周围类中的迭代状态,因此也会导致第二个循环立即终止。
class MySequence implements Iterable<MyElem> {
// ... some reference to data
final Iterator<MyElem> it = data.iterator();
// Wrong: iteration state outside returned iterator
public Iterator<MyElem> iterator() {
return new Iterator<MyElem>() {
public boolean hasNext() {
return it.hasNext();
}
public MyElem next() {
return transformElem(it.next());
}
public void remove() {
// ...
}
};
}
}
相反,代码应按如下方式编写,以便每次调用 iterator()
时都能正确地提供一个从头开始的新迭代器。
class MySequence implements Iterable<MyElem> {
// ... some reference to data
public Iterator<MyElem> iterator() {
return new Iterator<MyElem>() {
// Correct: iteration state inside returned iterator
final Iterator<MyElem> it = data.iterator();
public boolean hasNext() {
return it.hasNext();
}
public MyElem next() {
return transformElem(it.next());
}
public void remove() {
// ...
}
};
}
}
参考¶
Java 语言规范:增强型 for 语句。
Java API 规范:接口 Iterable<T>,接口 Iterator<T>,接口 DirectoryStream<T>。