关闭java.util.Iterator

r55awzrz  于 2023-05-05  发布在  Java
关注(0)|答案(8)|浏览(180)

我已经实现了一个定制的java.util.Iterator,它使用了一个资源,该资源应该在最后使用close()方法释放。该资源可以是java.sql.ResultSetjava.io.InputStream等...

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

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

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

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

***************我会给予大家一个(+1)。当hasNext()**返回false时,我已经关闭了Iterator。我的问题是当循环迭代在最后一次迭代之前中断时,如我的示例所示。

soat7uwm

soat7uwm1#

创建一个自定义迭代器,实现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));
zvms9eto

zvms9eto2#

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

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

并明确记录:

  • 如果在确保调用close self之前需要释放迭代器,则此迭代器将在hasNext()返回false时调用close()*

例如:

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循环。

hyrbngr7

hyrbngr73#

问题是最后的条件。我们经常迭代一个完整的集合或数据集,所以我们现在 * 在最后 *,没有数据可供读取。
但是如果我们在到达End Of Data之前设置一个break,迭代器就不会结束,也不会关闭。
一种解决方法是在构造过程中将数据源的内容缓存在迭代器中,然后关闭资源。因此迭代器不会在打开的资源上工作,而是在缓存的数据上工作。

xam8gpfp

xam8gpfp4#

你可以在终结器中关闭它,但它不会给予你想要的行为。只有当垃圾回收器要清理对象时,才会调用终结器,因此资源可能保持打开状态。更糟糕的是,如果有人占用了你的迭代器,它将永远不会关闭。
有可能在第一次调用hasNext()并返回false时关闭流。这仍然不能保证做到这一点,因为有些人可能只迭代第一个元素,而不再为此费心。
实际上,我认为在处理外部库时,您需要自己管理它。您将对使用iterable的方法进行这些调用,那么为什么不在完成后自己关闭它呢?资源管理不是您可以强加给不了解任何更好的外部库的东西。

v8wbuo2f

v8wbuo2f5#

如果可能,将迭代器 Package 在Stream中,这将给予您可以访问流的onClose方法。然后你应该把你的关闭逻辑移到那个方法中,并在那里做你的清理工作。
示例:

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

f5emj3cl6#

只需定义自己的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();
    }
};
fquxozlt

fquxozlt7#

在我的一个项目中,我也遇到了类似的问题,使用了像对象流这样的迭代器。为了覆盖迭代器没有完全消耗的时间,我还需要一个close方法。最初,我只是扩展了Iterator和Closable接口,但深入挖掘一下,java 1.7中引入的try-with-resources语句,我认为提供了一种实现它的整洁方法。
您扩展了Iterator和AutoCloseable接口,实现了Close方法,并在try-with-resources中使用了Iterator。一旦迭代器超出范围,运行时将为您调用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 { }
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;
}

{\fnSimHei\bord1\shad1\pos(200,288)}
下面是一个使用try -with-resource的例子:
public class MyIterator {

/**
 * 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;
}

{\fnSimHei\bord1\shad1\pos(200,288)}

vaqhlq81

vaqhlq818#

根据合同,迭代器不需要关闭,所以你不能实现它需要更多的东西给用户。
我使用的一个解决方案是要求将关闭操作调度到“environment”并由“environment”执行。例如,在Web服务器中,我在请求属性中保存了一个可关闭的堆栈。有一个servlet过滤器,它基本上在请求端关闭堆栈中的所有可关闭对象。在这样的环境中,您可以在打开ClosableIterator时立即将其添加到该堆栈中,用户在请求处理的上下文中使用该对象,并且在请求结束时,过滤器关闭要关闭的内容。
另一种解决方案是将上次活动的时间戳与对象本身一起保存。这里有一个可关闭的列表。线程可以周期性地迭代列表以收集不活动/超时的可关闭项来关闭被放弃的可关闭项。
此外,你可以改变主意,从基于拉的方法转向基于推的方法。你可以创建一个Observable,而不是创建一个Iterator。

相关问题