Java 使用嵌套迭代器在两层结构上迭代
我有以下两层Java 使用嵌套迭代器在两层结构上迭代,java,xml,iterator,stax,Java,Xml,Iterator,Stax,我有以下两层XML结构。盒子列表,每个盒子包含一个抽屉列表 <Boxes> <Box id="0"> <Drawers> <Drawer id="0"/> <Drawer id="1"/> ... </Drawers> </Box> <Box id="1"> ...
XML
结构。盒子列表,每个盒子包含一个抽屉列表
<Boxes>
<Box id="0">
<Drawers>
<Drawer id="0"/>
<Drawer id="1"/>
...
</Drawers>
</Box>
<Box id="1">
...
</Box>
</Boxes>
在那些迭代器的掩护下
我正在使用StAX
,它们都在访问相同的底层XMLStreamReader
。如果调用BoxIterator.next()
,将影响后续调用DrawerIterator.next()
时返回的结果,因为光标将移动到下一个框
这是否违反了迭代器的契约?
是否有更好的方法使用StAX
迭代两级结构
这是否违反了迭代器的契约
没有
Java迭代器
强加了两个“契约”。第一个契约是Java接口本身,它声明了3个方法:hasNext()
、next()
和remove()
。任何实现该迭代器接口的类都必须定义这些方法
第二份合同定义了以下人员的行为:
hasNext()
[…]如果迭代包含更多元素,则返回true。[…]next()
返回迭代中的下一个元素[如果迭代没有更多元素,则]抛出NoSuchElementException
这就是全部合同
的确,如果底层的XMLStreamReader
是高级的,它可能会弄乱您的BoxIterator
和/或DrawerIterator
。或者,在错误的点调用BoxIterator.next()
和/或DrawerIterator.next()
可能会破坏迭代。但是,正确使用,例如在上面的示例代码中,它可以正常工作并大大简化代码。您只需要记录迭代器的正确用法
作为一个具体的例子,该类实现了迭代器(Iterator),但还有许多其他方法可以推进底层流。如果存在由迭代器
类强加的更强契约,那么扫描器
类本身将违反它
正如注释中所指出的,boxList
不应属于类BoxIterator实现迭代器,Iterable
。你真的应该:
class BoxList implements Iterable<Box> { ... }
class BoxIterator implements Iterator<Box> { ... }
BoxList boxList = ...;
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId()
}
}
在这里,调用两次boxList.iterator()
,以创建两个独立的iterator
实例,对框列表进行两次迭代。由于可以多次迭代boxList
,因此每次迭代都需要一个新的迭代器实例
在代码中:
BoxIterator boxList = new BoxIterator(xml_stream);
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId();
}
}
因为您正在对流进行迭代,所以(不回放流或存储提取的对象)无法再次对相同的节点进行迭代。不需要第二类/对象;同一个对象可以同时作为Iterable和Iterator。。。这将为您保存一个类/对象
话虽如此,过早优化是万恶之源。一个类/对象的节省不值得可能的混淆;您应该将BoxIterator
拆分为一个BoxList implements Iterable
,BoxIterator implements Iterator
,hasNext()
可能返回true
,但next()
可能抛出NoTouchElementException
,因此它有可能破坏契约
hasNext()
的合同是:
如果迭代包含更多元素,则返回true。(换句话说,如果next()将返回元素而不是引发异常,则返回true。)
但是在调用hasNext()
和next()
之间,另一个迭代器可能已经移动了流的位置,这样就没有更多的元素了
但是,按照您使用它的方式(嵌套循环),您不会遇到中断
如果要将迭代器传递给另一个进程,则可能会遇到这种破坏。如果您仔细地实现/覆盖next()
&hasNext(),它看起来不会破坏契约
通过实现迭代器
接口,在框迭代器
和抽屉迭代器
中使用方法。不用说,需要注意的明显条件是hasNext()
应该返回true
如果next()
返回一个元素,而false
如果next()
给出异常
但我不明白的是,为什么要让BoxIterator
实现Iterable
BoxIterator实现Iterator,Iterable
因为从Iterable
接口重写Box
的iterator()
方法总是会返回BoxIterator
的实例。如果您没有任何其他目标,那么将此功能封装在BoxIterator
中是没有意义的。您的代码块的唯一设计问题是BoxIterator
同时实现了迭代器和Iterable
。通常,每次调用Iterator()
方法时,Iterable
对象都会返回新的有状态的Iterator
。正因为如此,两个迭代器之间应该没有干扰,但您需要一个状态对象来正确实现从内部循环退出(可能您已经有了,但为了清楚起见,我必须提到它)
状态对象将充当解析器的代理,有两个方法popEvent和peekEvent。在peek上,迭代器将检查最后一个事件,但不会使用它。在pop上,他们将消耗最后一个事件
BoxIterable#iterator()
将使用StartElement(box),然后返回iterator
BoxIterator#hasNext()
将查看事件并将其弹出,直到收到StartElement或EndElement。只有当Star
List<Box> boxList = Arrays.asList(box1, box2, box3, box4);
for(Box box : boxList) {
// Do something
}
for(Box box : boxList) {
// Do some more stuff
}
BoxIterator boxList = new BoxIterator(xml_stream);
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId();
}
}
BoxIterable boxList;
/*
* boxList must be an BoxIterable, which on call to iterator() returns
* new BoxIterator initialized with current state of STaX parser
*/
for (Box box : boxList) {
/*
* on following line new iterator is created and initialized
* with current state of parser
*/
for (Drawer drawer : box) {
drawer.getId()
}
}