CodeQL 文档

包装迭代器的 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

点击查看 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 循环中),这样做可以更轻松地找到错误。

建议

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

参考

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