开发者

Closing a java.util.Iterator

开发者 https://www.devze.com 2023-01-07 03:12 出处:网络
I\'ve implemented a custom java.util.Iteratorusin开发者_StackOverflow中文版g a resourcethat should be released at the end using a close() method. That resource could be a java.sql.ResultSet, a java.io

I've implemented a custom java.util.Iterator usin开发者_StackOverflow中文版g a resource that should be released at the end using a close() method. That resource could be a java.sql.ResultSet, a java.io.InputStream etc...

public interface CloseableIterator<T> extends Iterator<T>
  {
  public void close();
  }

Some external libraries using this iterator might not know that it must be closed. e.g:

public boolean isEmpty(Iterable<T> myiterable)
 {
 return myiterable.iterator().hasNext();
 }

In that case, is there a way to close this iterator?

Update: many thanks for the current answers . I'll give a (+1) to everybody. I do already close the Iterator when hasNext() returns false. My problem is when the loop iterating breaks before the last iteration as it is shown in my example.


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


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.


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.


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.


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


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


I had a similar issue in one of my projects using an Iterator like an Object stream. To cover the times the Iterator is not fully consumed, I also needed a close method. Initially I simply extended Iterator and Closable interfaces, but digging a little deeper, the try-with-resources statement introduced in java 1.7, I thought provided a tidy way of implementing it.

You extend Iterator and AutoCloseable interfaces, implement the Close method and use the Iterator in a try-with-resources. The Runtime will call close for you once the Iterator is out of scope.

https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html

https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html

For example

The Interface: public interface MyIterator<E> extends Iterator<E>, AutoCloseable { }

An Implementation of it:` public class MyIteratorImpl implements MyIterator {

private E nextItem = null;

@Override
public boolean hasNext() {
    if (null == nextItem)
        nextItem = getNextItem();

    return null != nextItem;
}

@Override
public E next() {
    E next = hasNext() ? nextItem : null;
    nextItem = null;
    return next;
}

@Override
public void close() throws Exception {
    // Close off all your resources here, Runtime will call this once 
       Iterator out of scope.
}

private E getNextItem() {
    // parse / read the next item from the under laying source.
    return null;
}

} `

And an example of using it with the try -with-resource:

` public class MyIteratorConsumer {

/**
 * Simple example of searching the Iterator until a string starting 
 *  with the given string is found.
 * Once found, the loop stops, leaving the Iterator partially consumed.
 * As 'stringIterator' falls out of scope, the runtime will call the 
 * close method on the Iterator.
 * @param search the beginning of a string
 * @return The first string found that begins with the search
 * @throws Exception 
 */
public String getTestString(String search) throws Exception {
    String foundString = null;

    try (MyIterator<String> stringIterator = new MyIteratorImpl<>()) {
        while (stringIterator.hasNext()) {
            String item = stringIterator.next();
            if (item.startsWith(search)) {
                foundString = item;
                break;
            }
        }

    }
    return foundString;
}

} `

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号