实现 Iterable 的迭代器¶
ID: java/iterator-implements-iterable
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 循环中),则这会使错误更容易找到。
建议¶
在使用 Iterator<T>
的自定义实现时,很容易添加 implements Iterable<T>
和 iterator()
的简单 return this;
实现以支持 foreach 语法。但是,这可能会隐藏一些细微的错误,因此不建议这样做。最好将两者分开,并使用仅实现 Iterable<T>
的主表示形式,而不包含任何迭代状态。然后,此对象可以在每次需要遍历时返回一个短暂的 Iterator<T>
。
如果由于某种原因这种重构不可取,那么 iterator()
方法至少应该在被调用多次时引发异常。
示例¶
以下示例未区分可迭代对象及其迭代器,因此导致第二个循环立即终止,没有任何效果。
class ElemIterator implements Iterator<MyElem>, Iterable<MyElem> {
private MyElem[] data;
private idx = 0;
public boolean hasNext() {
return idx < data.length;
}
public MyElem next() {
return data[idx++];
}
public Iterator<MyElem> iterator() {
return this;
}
// ...
}
void useMySequence(Iterable<MyElem> s) {
// do some work by traversing the sequence
for (MyElem e : s) {
// ...
}
// do some more work by traversing it again
for (MyElem e : s) {
// ...
}
}
最佳解决方案是沿着以下思路进行重构,其中 Iterable
类用于传递对数据的引用。这允许 Iterator
实例短暂存在,并避免共享迭代状态。
class ElemSequence implements Iterable<MyElem> {
private MyElem[] data;
public Iterator<MyElem> iterator() {
return new Iterator<MyElem>() {
private idx = 0;
public boolean hasNext() {
return idx < data.length;
}
public MyElem next() {
return data[idx++];
}
};
}
// ...
}
如果如上所述的重构过于繁琐或不可取,则可以插入一个防护措施,如下所示。使用防护措施可确保多次迭代尽早失败,从而更容易找到任何相关的错误。此解决方案不如上述重构理想,但仍然比原始解决方案有所改进。
class ElemIterator implements Iterator<MyElem>, Iterable<MyElem> {
private MyElem[] data;
private idx = 0;
private boolean usedAsIterable = false;
public boolean hasNext() {
return idx < data.length;
}
public MyElem next() {
return data[idx++];
}
public Iterator<MyElem> iterator() {
if (usedAsIterable || idx > 0)
throw new IllegalStateException();
usedAsIterable = true;
return this;
}
// ...
}
参考¶
Java 语言规范:增强型 for 语句。
Java API 规范:接口 Iterable<T>、接口 Iterator<T>、接口 DirectoryStream<T>。