使用嵌套迭代器迭代两级结构

2022-09-04 22:05:08

我有以下两级结构。一个框列表,每个框包含一个抽屉列表。XML

<Boxes>
    <Box id="0">
        <Drawers>
            <Drawer id="0"/>
            <Drawer id="1"/>
            ...
        </Drawers>
    </Box>
    <Box id="1">
...
    </Box>
</Boxes>

我正在解析它,并通过两个来公开结构:StAXIterators

  1. BoxIterator implements Iterator<Box>, Iterable<Box>
  2. Box implements Iterable<Drawer>
  3. DrawerIterator implements Iterator<Drawer>

然后,我可以执行以下操作:

BoxIterator boxList;
for (Box box : boxList) {
  for (Drawer drawer : box) {
    drawer.getId()
  }
}

在我正在使用的那些人的引擎盖下,他们俩都在访问相同的底层。如果我调用它,它将影响将在后续调用中返回的结果,因为光标将移动到下一个框。IteratorsStAXXMLStreamReaderBoxIterator.next()DrawerIterator.next()

这是否打破了?有没有更好的方法来迭代两级结构?IteratorStAX


答案 1

这是否打破了?Iterator

哈哈

Java强加了两个“契约”。第一个协定是 Java 接口本身,它声明了 3 个方法:、 和 。实现此接口的任何类都必须定义这些方法。IteratorhasNext()next()remove()Iterator

第二个协定定义了迭代器的行为:

hasNext()[...] 如果迭代具有更多元素,则返回 true。[...] 返回迭代中的下一个元素 [,如果迭代没有更多元素,则抛出] 。next()NoSuchElementException

这就是整个合同。

确实,如果底层是高级的,它可能会弄乱您的和/或.或者,调用和/或在错误的点可能会弄乱迭代。但是,如果使用得当,例如在上面的示例代码中,它可以正常工作并大大简化代码。您只需要记录迭代器的正确用法。XMLStreamReaderBoxIteratorDrawerIteratorBoxIterator.next()DrawerIterator.next()

作为一个具体示例,Scanner 类实现了 ,但还有许多其他方法可以推进底层流。如果存在由该类强加的更强的契约,那么该类本身将违反它。Iterator<String>IteratorScanner


正如伊万在评论中指出的那样,不应该是类型。你真的应该有:boxListclass 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()
  }
}

虽然让一个类同时实现两者,并且对于您的用例来说在技术上没有错误,但它可能会导致混淆。IterableIterator

在另一个上下文中考虑此代码:

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();
  }
}

由于您正在迭代流,因此无法(不倒带流或存储提取的对象)再次循环访问相同的节点。不需要第二个类/对象;同一个对象可以同时充当迭代器和迭代器...这为您节省了一个类/对象。

话虽如此,过早优化是万恶之源。一个类/对象的节省不值得可能的混淆;您应该拆分为 、 和 。BoxIteratorBoxList implements Iterable<Box>BoxIterator implements Iterator<Box>


答案 2

它有可能破坏合同,原因可能是返回,但可能会抛出一个.hasNext()truenext()NoSuchElementException

合同是:hasNext()

如果迭代具有更多元素,则返回 true。(换句话说,如果 next() 将返回一个元素而不是引发异常,则返回 true。

但是,在调用 和 之间,另一个迭代器可能会移动流的位置,使得不再有元素。hasNext()next()

但是,以您使用它的方式(嵌套循环),您不会遇到破损。

如果要将迭代器传递给另一个进程,则可能会遇到此中断。