我遇到了许多需要迭代器的问题。通常,它们是简单的事情,您已经拥有了可以遵循的基础数据结构。其他时候,它变得更加复杂。
一个例子是使用有序遍历在没有父链接的情况下迭代BST。这要求您执行以下操作:
您可以在hasNext()或next()中找到下一个节点。您还可以在构造函数中或第一次调用hasNext()时找到第一个节点。
我的问题
是否有标准或最佳实践可用于在迭代器实现中执行大部分工作?一种方式比另一种“更清洁”吗?
答案 0 :(得分:6)
首先,Iterator
的合同要求hasNext
如果有更多元素则返回true
,next
会在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开始也支持LinkedList
和push
,以及每次调用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
会导致多个客户端错误,因为它对于您正在迭代的内容并不明显),您应该考虑另一种实现后续迭代的方法。在任何情况下,您都应该小心设计迭代器。每个迭代器都与其数据结构紧密相连,应该一起设计。