Закрытие java.util.Iterator - PullRequest
       3

Закрытие java.util.Iterator

31 голосов
/ 15 июля 2010

Я реализовал пользовательский java.util.Iterator , используя ресурс, который должен быть освобожден в конце, используя метод close().Этот ресурс может быть java.sql.ResultSet , java.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.Моя проблема в том, что итерация цикла прерывает до последней итерации, как показано в моем примере.

Ответы [ 7 ]

16 голосов
/ 06 апреля 2015

Создайте пользовательский итератор, который реализует интерфейс AutoCloseable

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

И затем используйте этот итератор в попытке с оператором ресурса .

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

Этот шаблон закроет базовый ресурс независимо от того, что произойдет: - после завершения оператора - и даже если сгенерировано исключение

Наконец, четко документируйте, как должен использоваться этот итератор.

Если вы не хотите делегировать закрытые вызовы, используйте стратегию push.например.с Java 8 лямбда:

dao.findAll(r -> process(r));
14 голосов
/ 15 июля 2010

В вашей реализации вы можете закрыть его самостоятельно, если когда итератор исчерпан.

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

И четко документ:

Этот итератор вызовет close (), когда hasNext () вернет false, если вам нужно удалить итератор перед тем, как вызывать 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.

9 голосов
/ 15 июля 2010

Проблема в условии в конце . Часто мы перебираем всю коллекцию или набор данных, поэтому на данный момент мы в конце , данных для чтения не осталось.

Но если мы установим разрыв цикла до того, как достигнем конца данных, итератор не завершится и не закроется.

Выходом может быть кэширование содержимого источника данных в итераторе во время построения и закрытие ресурса. Поэтому итератор будет работать не с открытым ресурсом, а с кэшированными данными.

7 голосов
/ 15 июля 2010

Вы можете закрыть его в финализаторе , но это не даст вам желаемого поведения. Ваш финализатор вызывается только тогда, когда сборщик мусора хочет очистить ваш объект, поэтому ваш ресурс может остаться открытым. Хуже того, если кто-то удержит ваш итератор, он никогда не закроется.

Существует возможность закрытия потока при первом вызове hasNext (), который возвращает false. Это все еще не гарантировано, поскольку кто-то может перебирать только первый элемент и никогда больше не беспокоиться об этом.

На самом деле, я думаю, вам придется управлять им самостоятельно при работе с внешней библиотекой. Вы собираетесь выполнять эти вызовы методов, которые используют итерируемое, так почему бы не закрыть его самостоятельно, когда вы закончите? Управление ресурсами - это не то, что вы можете просто навязать внешней библиотеке, которая не знает ничего лучше.

4 голосов
/ 09 июля 2013

Просто определите свой собственный подчиненный интерфейс 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();
    }
};
1 голос
/ 03 февраля 2018

Если возможно, оберните итератор в поток, что даст вам доступ к методу onClose потоков.Затем вы должны перенести свою близкую логику в этот метод и выполнить очистку там.

Пример:

StreamSupport.stream(Spliterators.spliteratorUnknownSize(
        new MyCustomIterator<T>(), 0), false).onClose(() -> {
    // close logic here
});
1 голос
/ 09 октября 2016

У меня была похожая проблема в одном из моих проектов с использованием итератора, подобного потоку объектов.Чтобы охватить время, когда итератор используется не полностью, мне также понадобился метод close.Первоначально я просто расширил интерфейсы Iterator и Closable, но немного углубившись в оператор try-with-resources, представленный в Java 1.7, я подумал, что предоставил удобный способ его реализации.

Вы расширяете интерфейсы Iterator и AutoCloseable,реализовать метод Close и использовать итератор в try-with-resources.Среда выполнения вызовет для вас закрытие, как только Итератор выйдет из области видимости.

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

}`

И пример использования его с try -with-ресурс:

`открытый класс 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;
}

}`

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...