我有以下两个级别XML
结构体。一个盒子列表,每个盒子包含一个抽屉列表。
<Boxes>
<Box id="0">
<Drawers>
<Drawer id="0"/>
<Drawer id="1"/>
...
</Drawers>
</Box>
<Box id="1">
...
</Box>
</Boxes>
我正在使用 StAX
解析它并通过两个 Iterators
暴露结构:
-
BoxIterator implements Iterator<Box>, Iterable<Box>
-
Box implements Iterable<Drawer>
-
DrawerIterator implements Iterator<Drawer>
然后我可以执行以下操作:
BoxIterator boxList;
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId()
}
}
在这些背后Iterators
我正在使用 StAX
并且他们都在访问相同的基础 XMLStreamReader
.如果我调用 BoxIterator.next()
它将影响随后调用 DrawerIterator.next()
时返回的结果。因为光标将移动到下一个框。
这是否违反了 Iterator
的契约(Contract)? ?
有没有更好的方法使用 StAX
迭代两级结构? ?
最佳答案
Does this break the contract of
Iterator
?
没有。
Java Iterator
强加两个“契约(Contract)”。第一个契约是 Java 接口(interface)本身,它声明了 3 个方法:hasNext()
, next()
, 和 remove()
.任何实现此 Iterator
的类接口(interface)必须定义这些方法。
第二个合约定义了 Iterator
的行为:
hasNext()
[...] returns true if the iteration has more elements. [...]next()
returns the next element in the iteration [and] throwsNoSuchElementException
if the iteration has no more elements.
这就是整个契约(Contract)。
确实,如果底层 XMLStreamReader
是高级的,它会弄乱你的BoxIterator
和/或 DrawerIterator
.或者,调用 BoxIterator.next()
和/或 DrawerIterator.next()
在错误的点可能会搞乱迭代。但是,正确使用,例如在上面的示例代码中,它可以正常工作并大大简化代码。您只需要记录迭代器的正确用法。
作为一个具体的例子, Scanner
类(class)工具Iterator<String>
,但还有许多其他方法可以推进基础流。如果 Iterator
强加了更强的契约(Contract)类,然后是 Scanner
类本身会违反它。
作为Ivan在评论中指出,boxList
不应为 class BoxIterator implements Iterator<Box>, Iterable<Box>
类型.你真的应该:
class BoxList implements Iterable<Box> { ... }
class BoxIterator implements Iterator<Box> { ... }
BoxList boxList = ...;
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId()
}
}
虽然一个类同时实现了 Iterable
和 Iterator
对于您的用例在技术上没有错误,但它可能会引起混淆。
在另一个上下文中考虑这段代码:
List<Box> boxList = Arrays.asList(box1, box2, box3, box4);
for(Box box : boxList) {
// Do something
}
for(Box box : boxList) {
// Do some more stuff
}
在这里,boxList.iterator()
被调用两次,创建两个独立的 Iterator<Box>
实例,用于迭代框列表两次。因为boxList
可以迭代多次,每次迭代都需要一个新的迭代器实例。
在您的代码中:
BoxIterator boxList = new BoxIterator(xml_stream);
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId();
}
}
因为您正在迭代一个流,所以您不能(不倒回流或存储提取的对象)第二次迭代相同的节点。不需要第二类/对象;同一个对象可以同时充当 Iterable 和 Iterator ...这为您节省了一个类/对象。
话虽如此,过早的优化是万恶之源。一个类/对象的节省不值得可能的混淆;你应该拆分BoxIterator
进入BoxList implements Iterable<Box>
, 和 BoxIterator implements Iterator<Box>
.
关于java - 使用嵌套迭代器迭代两级结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38630926/