Подтвердить что ты не робот

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

Я реализовал пользовательский 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) всем. Я уже закрываю Iterator, когда hasNext() возвращает false. Моя проблема заключается в том, что повторение цикла прерывается до последней итерации, как показано в моем примере.

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

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

И ясно документ:

Этот итератор будет вызывать close(), когда hasNext() возвращает false, если вам нужно удалить итератор, прежде чем вы убедитесь, что вы запустили self

Пример:

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

Btw, вы могли бы, пока вы там, вы могли бы реализовать Iterable и использовать расширенный цикл.

Ответ 3

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

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

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

Ответ 4

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

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

И затем используйте этот итератор в попробуйте с помощью оператора ресурсов.

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

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

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

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

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

Ответ 5

Просто определите свой собственный под-интерфейс 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();
    }
};

Ответ 6

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

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

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

} `