I\'ve implemented a custom java.util.Iterator using a resource that should be released at the end using a close()
method. That resource could
The problem is the condition at the end. Often we iterate over a full collection or data set, so we're at the end at the moment, there's no data left to read.
But if we set a break out of the loop before we reached the End Of Data, the iterator wouldn't come to an end and wouldn't be closed.
A way out could be to cache the content of the data source in the iterator during construction and close the resource. So the iterator would not work on the opened resource but on cached data.
Create a custom iterator which implement the AutoCloseable interface
public interface CloseableIterator<T> extends Iterator<T>, AutoCloseable {
}
And then use this iterator in a try with resource statement.
try(CloseableIterator iterator = dao.findAll()) {
while(iterator.hasNext()){
process(iterator.next());
}
}
This pattern will close the underlying resource whatever happens: - after the statement complete - and even if an exception is thrown
Finally, clearly document how this iterator must be used.
If you do not want to delegate the close calls, use a push strategy. eg. with java 8 lambda:
dao.findAll(r -> process(r));
You could close it in a finalizer but it's not going to give you the behavior you want. Your finalizer is called only when the garbage collector wants to cleanup your object, so your resource might remain open. Worse, if someone holds onto your iterator, it'll never close.
There's a possibility of closing the stream on the first call to hasNext() that returns false. That's still not guaranteed to do it since someone might iterate only the first element and never bother with it again.
Really, I think you'll need to manage it yourself when dealing with an external library. You're going to make those calls to the methods that use the iterable, so why not close it yourself when you're done? Resource management is not something you can just impose on an external library that doesn't know any better.
In your implementation you could close it your self, if when the iteratoror is exhausted.
public boolean hasNext() {
....
if( !hasNext ) {
this.close();
}
return hasNext;
}
And clearly document:
This iterator will invoke close() when hasNext() return false, if you need to dispose the iterator before make sure you call close your self
example:
void testIt() {
Iterator i = DbIterator.connect("db.config.info.here");
try {
while( i.hasNext() {
process( i.next() );
}
} finally {
if( i != null ) {
i.close();
}
}
}
Btw, you could while you're there you could implement Iterable and use the enhanced for loop.
If possible, wrap the iterator in a Stream, which will give you access to the onClose
method of streams. Then you should move your close logic into that method and do your cleanup there.
Example:
StreamSupport.stream(Spliterators.spliteratorUnknownSize(
new MyCustomIterator<T>(), 0), false).onClose(() -> {
// close logic here
});
Just define your own sub-interface of Iterator that includes a close method, and make sure you use that instead of the regular Iterator class. For example, create this interface:
import java.io.Closeable;
import java.util.Iterator;
public interface CloseableIterator<T> extends Iterator<T>, Closeable {}
And then an implementation might look like this:
List<String> someList = Arrays.asList( "what","ever" );
final Iterator<String> delegate = someList.iterator();
return new CloseableIterator<String>() {
public void close() throws IOException {
//Do something special here, where you have easy
//access to the vars that created the iterator
}
public boolean hasNext() {
return delegate.hasNext();
}
public String next() {
return delegate.next();
}
public void remove() {
delegate.remove();
}
};