Does this break the contract of Iterator
?
No.
The Java Iterator
imposes two "contracts". The first contract is the Java interface itself, which declares 3 methods: hasNext()
, next()
, and remove()
. Any class which implements this Iterator
interface must define those methods.
The second contract defines the behaviour of the 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.
That is the entire contract.
It is true that if the underlying XMLStreamReader
is advanced, it can mess up your BoxIterator
and/or DrawerIterator
. Alternately, calling BoxIterator.next()
and/or DrawerIterator.next()
at the wrong points could mess up the iteration. However, used correctly, such as in your example code above, it works properly and greatly simplifies the code. You just need to document the proper usage of the iterators.
As a concrete example, the Scanner
class implements Iterator<String>
, and yet has many, many other methods that advance the underlying stream. If there existed a stronger contract imposed by the Iterator
class, then the Scanner
class itself would be violating it.
As Ivan points out in the comments, boxList
should not be of type class BoxIterator implements Iterator<Box>, Iterable<Box>
. You really should have:
class BoxList implements Iterable<Box> { ... }
class BoxIterator implements Iterator<Box> { ... }
BoxList boxList = ...;
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId()
}
}
While having one class implement both Iterable
and Iterator
is not technically wrong for your use case, it can cause confusion.
Consider this code in another context:
List<Box> boxList = Arrays.asList(box1, box2, box3, box4);
for(Box box : boxList) {
// Do something
}
for(Box box : boxList) {
// Do some more stuff
}
Here, boxList.iterator()
is called twice, to create two separate Iterator<Box>
instances, for iterating the list of boxes twice. Because the boxList
can be iterated over multiple times, each iteration requires a new iterator instance.
In your code:
BoxIterator boxList = new BoxIterator(xml_stream);
for (Box box : boxList) {
for (Drawer drawer : box) {
drawer.getId();
}
}
because you are iterating over a stream, you can't (without rewinding the stream, or storing the extracted objects) iterate over the same nodes a second time. A second class/object is not needed; the same object can act as both Iterable and Iterator ... which saves you one class/object.
Having said that, premature optimization is the root of all evil. The savings of one class/object is not worth the possible confusion; you should split BoxIterator
into a BoxList implements Iterable<Box>
, and BoxIterator implements Iterator<Box>
.
Box.iterator
returns a newDrawerIterator
and if that is so the contract won't be broken, since theDrawerIterator
should return only elements inside the current box anyways. – AnyoneBox.iterator()
will return the sameDrawerIterator
on every call, since they will all be accessing the same underlying stream anyway. This implies that even aDrawerIterator
returned by a past call toBox.iterator()
will be magically advanced. All will be accessing the underlying stream at the same cursor position, always. – UnhitchhasNext()
should return false. – AnyoneDrawerIterator
that would have acurrentBox
field that would automatically be updated. Yet this would still have the problem that all the iterators would be accessing the same underlying stream, so advancing one, would advance all the others. – UnhitchDrawer
ids orDrawers
? Are you not interested in theBox
ids? Perhaps you can use StAX in another way. StAX can parse the file and generate events whenever it finds the start or the end of an element. All you have to do is check whether that element is aBox
or aDrawer
. – EbenezerBox
all I need is the ID. From theDrawer
I need some more information which is not present in my simplified example. I'm usingStAX
as you suggested. When I find aBox
I just read theID
and save it. Then when I go through theDrawers
I know whichBox
they belong to. – Unhitch