关闭java.util.Iterator

时间:2010-07-15 16:56:53

标签: java resources iterator

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

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

使用此迭代器的一些外部库可能不知道必须关闭它。 e.g:

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

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

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

7 个答案:

答案 0 :(得分:16)

创建一个实现AutoCloseable interface

的自定义迭代器
public interface CloseableIterator<T> extends Iterator<T>, AutoCloseable {
}

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

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

无论发生什么情况,此模式都将关闭底层资源: - 声明完成后 - 即使抛出异常

最后,清楚地记录必须如何使用此迭代器。

如果您不想委派近距离通话,请使用推送策略。例如。用java 8 lambda:

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

答案 1 :(得分:14)

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

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

明确记录:

  

当hasNext()返回false时,此迭代器将调用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循环。

答案 2 :(得分:9)

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

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

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

答案 3 :(得分:7)

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

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

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

答案 4 :(得分:4)

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

import java.io.Closeable;
import java.util.Iterator;

public interface CloseableIterator<T> extends Iterator<T>, Closeable {}

然后实现可能如下所示:

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

答案 5 :(得分:1)

我在我的一个项目中使用像对象流这样的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/AutoCloseable.html

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

例如

界面: public interface MyIterator<E> extends Iterator<E>, AutoCloseable { }

它的实现:` 公共类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:

的一个例子

` 公共类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;
}

} `

答案 6 :(得分:1)

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

示例:

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