【问题标题】:Returning the same object from Iterator<?> [duplicate]从 Iterator<?> 返回相同的对象 [重复]
【发布时间】:2026-02-01 23:55:01
【问题描述】:

它是否有效 - 即,在联系人内,Iterator 返回与先前迭代相同的(可变)对象,或者具有共享可变状态的新对象作为先前对象?也就是说,可以调用next() 来使先前调用返回的对象无效,只要它记录在案?

我完全理解这不是一个好主意一般来说,我也没有习惯。

但是,在某些情况下,重用相同的底层对象或具有共享内部结构的新对象(因此可能会使先前返回的对象的内部结构无效)在某些情况下具有重要且可衡量的意义。一个很好的例子是当从迭代器返回的对象以一定的偏移量和长度指向包含Iterable的缓冲区数组时,当检索后续对象时,该数组可能会被覆盖。

当然,这违反了最小惊讶原则,并且可能对客户不友好 - 但我很好奇它是否违反了Iterator 的联系方式 - 只要我记录下来,我是否符合规则?

javadoc 似乎是针对集合编写的,其中next() 调用之间的共享状态可能毫无意义,但Iterator 是一个通用接口(您可以在没有支持集合的情况下实现,甚至没有支持任何东西)。

基本上,我试图在性能方面实现与享元模式相当的东西,但要保留 Java 迭代器的优点。

遵循此模式的迭代器可以安全地以下列方式使用:

for (Foo foo : FooIterable) {
  if (foo.method()) {
    System.println("ha!");
  }
}

因为foo 的每个实例都不会从一个迭代逃逸到另一个迭代。但是,以下内容一般不会起作用:

List<Foo> allTheFoos = ...;
for (Foo foo : FooIterable) {
  allTheFoos.add(foo);
}

for (Foo foo : allTheFoos) {
  ...
}

由于第一个循环中的foos 被存储起来,因此逃脱了它们的迭代。当您稍后访问它们时,它们的状态可能无效(例如,它们可能看起来都像从初始迭代返回的最后一个 foo

【问题讨论】:

  • 为我辩护,我在这里和 Google 上进行了搜索,但我的 search-term-fu 今天显然很弱。

标签: java performance optimization iterator


【解决方案1】:

一个List 可以多次包含同一个对象,并且它的Iterator 将在它出现在列表中的位置返回这个对象。因此,这可能不是合同禁止的事情。 Set 不应包含重复项,因此不会返回相同的对象两次。

集合永远不会修改或使其成员无效,但是在迭代器本身内部存在与您的情况类似的情况:如果集合已被修改,它将变为无效(并在调用时引发异常)。这可能暗示了当返回的对象变得无效时要执行的解决方案。

也许您的迭代可以更好地返回有关该更改对象的包装器,以确保对先前返回的、现在无效的包装器的所有调用都抛出异常。

【讨论】:

  • 对,我猜第一句话不够窄——我同意在那种情况下返回同一个对象是可以的(返回对象的第二个副本不会影响第一个)——但是返回较晚的对象会使较早的对象无效的情况呢?
  • 是的,我想到了。你甚至不一定需要一个包装器,可能只是一个 modCount 或对象中的某些东西,与迭代器的 modcount 进行比较,如果它们不相同则抛出(即,如果对象逃脱了它的迭代)。但是,如果共享状态通过对象上的某些方法公开,这并不总是可行的 - 例如,如果它返回一个没有防御副本的数组,您将无法检测到以后对它的访问。