CodeQL 文档

实现 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

单击以在 CodeQL 存储库中查看查询

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;
  }
  // ...
}

参考

  • ©2025GitHub, Inc.
  • 条款
  • 隐私