用Java设计迭代器

时间:2015-03-15 14:09:28

标签: java iterator iterable

我遇到了许多需要迭代器的问题。通常,它们是简单的事情,您已经拥有了可以遵循的基础数据结构。其他时候,它变得更加复杂。

一个例子是使用有序遍历在没有父链接的情况下迭代BST。这要求您执行以下操作:

  • 在构造函数中创建一个堆栈。
  • 迭代到最左边的节点。
  • 存储有更多节点可供访问,以便从hasNext()返回。
  • 存储下一个要访问的节点,以便从next()轻松返回。

您可以在hasNext()或next()中找到下一个节点。您还可以在构造函数中或第一次调用hasNext()时找到第一个节点。


我的问题

是否有标准或最佳实践可用于在迭代器实现中执行大部分工作?一种方式比另一种“更清洁”吗?

2 个答案:

答案 0 :(得分:6)

首先,Iterator的合同要求hasNext如果有更多元素则返回truenext会在hasNext()==false时抛出异常。< / p>

这意味着使用迭代器有两种样式:while (it.hasNext()) it.next()try { while (true) it.next(); } catch ...。后者不是一个好习惯,但必须得到支持。我之所以提到这一点是因为你不能依赖hasNext之前被调用的next。我发现这个要求通常是迭代器实现中不必要的复杂性的罪魁祸首。

我的选择是拥有一个next值的局部变量。如果next==null下一个值未知(并且我们必须找到它),或者我们已经到达迭代结束(hasNext()将返回false并且next()将失败)。还要考虑当下一个值未知时,我们可能在迭代结束时,但我们还没有实现它。

Node next;

public boolean hasNext() {
   //if the next value already known, do nothing
   if (next==null) {         
     //otherwise lookup the next value
     next=findNext();
   }
   //return true if the next value was found
   return next!=null;
}

public Node next() {
  if (next==null&&!hasNext()) {
     //here we have reached the end of the iteration
     throw new NoSuchElementException();
  } else {
     //either we alredy knowed the next element 
     //or it was found by hasNext
     Node result = next;
     next=null;
     return result;
  }   
}

private Node findNext() {
   //the actual iteration
}

关于顺序遍历的情况,你应该保持一个堆栈(请注意Stack的实现是基于数组的并且是同步的,可以探讨的是使用Dequeue如{ {1}},从Java 6开始也支持LinkedListpush,以及每次调用pop时知道如何恢复迭代的辅助状态。

答案 1 :(得分:2)

BST按顺序遍历可以通过简单的递归DFS(左子 - &gt;节 - &gt;右子)来实现。

回答你的问题:总的来说,我认为迭代器设计没有“最佳实践”,因为你的数据结构可能是任意复杂的。一些常见的规则:

  • 在任何情况下,您的迭代器都必须支持hasNext()next()操作。如果您的数据结构是不可变的(或者删除不典型),remove()方法应抛出OperationNotSupportedException()
  • next()方法应在实施开始时检查hasNext()值,如果NoSuchElementException返回hasNext()则抛出false
  • 如果您的数据结构有一个迭代器,它应该实现Iterable<Type>接口并实现public Iterator<Type> iterator()方法。
  • 因此,如果您实现Iterable接口,客户端代码可以使用foreach运算符来处理您的数据结构。如果不希望这样的行为(例如,使用具有二分图结构的foreach会导致多个客户端错误,因为它对于您正在迭代的内容并不明显),您应该考虑另一种实现后续迭代的方法。
  • 如果您的数据结构使用list或set,则迭代器实现可以使用适当的collection iterator。

在任何情况下,您都应该小心设计迭代器。每个迭代器都与其数据结构紧密相连,应该一起设计。