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

标签 java xml iterator stax

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

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

我正在使用 StAX 解析它并通过两个 Iterators 暴露结构:

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

在这些背后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] throws NoSuchElementException 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()
  }
}

虽然一个类同时实现了 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();
  }
}

因为您正在迭代一个流,所以您不能(不倒回流或存储提取的对象)第二次迭代相同的节点。不需要第二类/对象;同一个对象可以同时充当 Iterable 和 Iterator ...这为您节省了一个类/对象。

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

关于java - 使用嵌套迭代器迭代两级结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38630926/

相关文章:

javascript - 检查是否有任何标签具有特定的内部 HTML

java - Android:如何绘制下载的位图,然后将其填充到 ImageView?

java - 将服务从 Axis 迁移到 Axis2 (Java) - 我应该使用哪个 MessageReceiver?

java - 使用 Ehcache 自定义序列化

java - 文本按钮显示为点而不是普通文本

android - 为什么我包含的库项目不能在 xml 文件中识别?正确使用图书馆项目

ruby 枚举器 : immediately skip multiple iterations (or start iterating from n)

java - ArrayList Iterator 迭代失败

java - 使用java屏蔽某个网站

java - Spring Boot JPA 保留具有空列的父级的子级