为什么标准Java迭代器没有peek()方法?

13

为什么基本的Java迭代器没有像peek()这样的函数,可以返回下一个元素而不会推进迭代器?


4
嗯,因为它不会。这似乎是一个难以令人满意地回答的问题。 - Elliott Frisch
1
Guava有一个Peeking Iterator,如果有帮助的话。 - shmosel
谢谢。我看到了Peeking Iterator,有点惊讶peek()不在标准Java迭代器中。 - La-comadreja
这并不是对于“为什么”的问题的答案,但是对于那些在此页面上寻找这样一个迭代器的人,我可以推荐 https://guava.dev/releases/22.0/api/docs/com/google/common/collect/Iterators.html#peekingIterator-java.util.Iterator- - Adrian Smith
3个回答

30
为什么标准Java迭代器没有peek()?
1.因为peek()不是通常描述的迭代器设计模式的一部分。
2.因为绝大多数迭代器使用案例不需要它。强制所有实现(包括无数自定义/第三方类)实现一个不必要的方法是个坏主意。
3.因为peek()方法对于惰性数据源的迭代器有潜在的语义影响。
4.因为实现peek()会影响迭代器在某些情况下的效率(内存,CPU),不管你是否真正使用它。
5.因为在某些晦涩的情况下,peek()会导致内存泄漏。
6.因为...KISS
但最终,真正的原因是...因为他们在2000年设计时就是这样设计的。我们不在设计辩论的房间里1

1 - 不管价值如何,似乎大多数其他语言都为它们的标准迭代器API做出了相同的决定。Rust似乎是个例外;请参见https://doc.rust-lang.org/std/iter/struct.Peekable.html

2 - ……基于一项高度非科学的“调查”,使用谷歌搜索。


如果您想要提供peek()功能的迭代器抽象层,可以扩展Iterator接口并自行实现迭代器。事实上,通用的带有peek的迭代器可以很容易地作为常规Iterator的包装器实现。
或者查找第三方API/实现,例如GuavaApache Commons等。

因为它不是原子性的。 - user207421
这是Iterator实现的一个属性。 - Stephen C
1
它从设计上永远不可能是原子性的。你需要使用 hasNext() 来修复下一个元素的可用性。 - user207421
1
在任何原子性有意义的程度上...它都可以被实现。但是这是在“迭代器”模式是非原子性的前提下;例如,如果it由多个线程共享,则if (it.hasNext()) e = it.next();不是原子性的。我认为这使得peek的原子性变得无关紧要。 - Stephen C

1
如果您真的需要一个带有peek()的迭代器,为什么不使用LinkedList作为您的初始数据结构?它提供了.peek()和.pop()来检索头部。

1
以下示例实现了一个带有peek方法的迭代器。 remove 的实现留给读者自行完成。
import java.util.Iterator;

/**
 * An Iterator with a peek method for just one value.
 */
public class PeekIterator<T> implements Iterator<T>
{
  private Iterator<T> iterator;

  public PeekIterator (Iterator<T> iterator) { this.iterator = iterator; }

  private boolean peeked = false;
  private T peeked_value = null;

  public boolean hasNext () { return iterator.hasNext () || peeked; }

  public T next ()
  {
    T value;
    if (peeked) {
      peeked = false;
      value = peeked_value;
    }
    else
      if (iterator.hasNext ())
        value = iterator.next();
      else
        value = null;
    return value;
  }

  public T peek ()
  {
    T value;
    if (peeked)
      value = peeked_value;
    else {
      peeked = true;
      if (iterator.hasNext ())
        peeked_value = iterator.next ();
      else
        peeked_value = null;
      value = peeked_value;
    }
    return value;
  }
}

1
hasNext 方法存在一个 bug。应该修改为 iterator.hasNext() || peeked,因为 peek 方法有可能会消耗基本迭代器的最后一个元素,导致基本迭代器为空。但是在 PeekIterator 中,我们仍然需要消耗那个被预先查看的元素。 - Consti P
仅添加iterator.hasNext() || peeked是不够的,因为如果在没有下一个元素时进行了peek操作,peeked仍将为true。如果在迭代器末尾进行peek操作后运行hasNext(),您将得到一个true响应。修复此问题的简单方法是在检查hasNext是否为true的else语句中添加peek = false: public T peek() { . . . if (iterator.hasNext()) peeked_value = iterator.next(); else peeked_value = null; peeked = false; value = peeked_value; } return value; } } - DanielWsk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接