关闭java.util.Iterator

我已经使用应该使用close()方法在最后发布的资源实现了自定义java.util.Iterator 。 该资源可以是java.sql.ResultSetjava.io.InputStream等……

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

使用此迭代器的某些外部库可能不知道必须关闭它。 例如:

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

在那种情况下,有没有办法关闭这个迭代器?

更新:非常感谢您当前的答案。 我会给每个人一个(+1)。 当hasNext()返回false时,我已经关闭了Iterator。 我的问题是当循环迭代最后一次迭代之前中断时,如我的例子中所示。

问题是最后的条件。 我们经常迭代完整的集合或数据集,所以我们现在已经结束了,没有数据可供阅读。

但是如果我们在到达数据结束之前设置了循环的中断,则迭代器将不会结束并且不会被关闭。

一种出路可能是在构造期间将数据源的内容缓存在迭代器中并关闭资源。 因此迭代器不会对打开的资源起作用,而是对缓存的数据起作用。

在你的实现中,如果迭代器耗尽,你可以将它自己关闭。

 public boolean hasNext() { .... if( !hasNext ) { this.close(); } return hasNext; } 

并清楚地记录:

当hasNext()返回false时,此迭代器将调用close(),如果您需要先处置迭代器,请确保调用close close your self

例:

 void testIt() { Iterator i = DbIterator.connect("db.config.info.here"); try { while( i.hasNext() { process( i.next() ); } } finally { if( i != null ) { i.close(); } } } 

顺便说一句,你可以在那里实现Iterable并使用增强的for循环。

创建一个实现AutoCloseable接口的自定义迭代器

 public interface CloseableIterator extends Iterator, AutoCloseable { } 

然后在try with resource语句中使用此迭代器。

 try(CloseableIterator iterator = dao.findAll()) { while(iterator.hasNext()){ process(iterator.next()); } } 

无论发生什么情况,此模式都将关闭底层资源: – 语句完成后 – 即使抛出exception

最后,清楚地记录了如何使用这个迭代器。

如果您不想委派关闭呼叫,请使用推送策略。 例如。 用java 8 lambda:

 dao.findAll(r -> process(r)); 

你可以在终结器中关闭它,但它不会给你你想要的行为。 只有当垃圾收集器想要清理对象时才会调用终结器,因此您的资源可能会保持打开状态。 更糟糕的是,如果有人抓住你的迭代器,它永远不会关闭。

有可能在第一次调用hasNext()时关闭流,返回false。 由于有人可能只迭代第一个元素并且再也不打扰它,所以仍然无法保证这样做。

真的,我认为在处理外部库时你需要自己管理它。 您将对使用iterable的方法进行调用,那么为什么不在完成后自行关闭它? 资源管理不是你可以强加给一个不知道更好的外部库的东西。

只需定义自己的Iterator子接口,其中包含close方法,并确保使用它而不是常规的Iterator类。 例如,创建此接口:

 import java.io.Closeable; import java.util.Iterator; public interface CloseableIterator extends Iterator, Closeable {} 

然后实现可能如下所示:

 List someList = Arrays.asList( "what","ever" ); final Iterator delegate = someList.iterator(); return new CloseableIterator() { 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(); } }; 

我在我的一个项目中使用像对象流这样的Iterator时遇到了类似的问题。 为了覆盖迭代器没有被完全消耗的时间,我还需要一个接近的方法。 最初我只是扩展了Iterator和Closable接口,但是深入挖掘java 1.7中引入的try-with-resources语句,我认为它提供了一种实现它的整洁方式。

您可以扩展Iterator和AutoCloseable接口,实现Close方法并在try-with-resources中使用Iterator。 一旦Iterator超出范围,Runtime将为您调用close。

https://docs.oracle.com/javase/7/docs/api/java/lang/Au​​toCloseable.html

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

例如

接口: public interface MyIterator extends Iterator, AutoCloseable { }

它的实现:`public class MyIteratorImpl实现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; } 

}

并将其与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 stringIterator = new MyIteratorImpl<>()) { while (stringIterator.hasNext()) { String item = stringIterator.next(); if (item.startsWith(search)) { foundString = item; break; } } } return foundString; } 

}

如果可能,将迭代器包装在Stream中,这将使您可以访问流的onClose方法。 然后你应该将你的关闭逻辑移动到该方法中并在那里进行清理。

例:

 StreamSupport.stream(Spliterators.spliteratorUnknownSize( new MyCustomIterator(), 0), false).onClose(() -> { // close logic here });