La fermeture d'un java.util.Itérateur

j'ai mis en place une coutume java.util.Iterator utiliser une ressource qui devrait être libérée à la fin en utilisant un close() méthode. Cette ressource pourrait être un java.SQL.ResultSet, a java.io.Entrants etc...

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

certaines bibliothèques externes utilisant cet itérateur pourraient ne pas savoir qu'il doit être fermé. e.g:

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

Dans ce cas, est-il un moyen de fermer cette itérateur?

mise à Jour: merci beaucoup pour les réponses actualisées . Je donnerai un (+1) à tout le monde. Je fais déjà proche de l'Itérateur quand hasNext () retourne false. Mon problème est quand la boucle itérative casse avant la dernière itération telle qu'elle apparaît dans mon exemple.

28
demandé sur Josh Crozier 2010-07-15 20:56:52

7 réponses

Le problème est la condition à la fin. Souvent nous itérons sur une collecte complète ou un ensemble de données, donc nous sommes à la fin pour le moment, il n'y a pas de données à lire.

mais si nous définissons une sortie de boucle avant que nous atteignions la fin des données, l'itérateur ne s'arrêterait pas et ne serait pas fermé.

une solution pourrait être de mettre en cache le contenu de la source de données dans l'itérateur pendant la construction et de fermer la ressource. De sorte que le iterator ne fonctionnerait pas sur la ressource ouverte mais sur les données en cache.

9
répondu Andreas_D 2010-07-15 17:12:58

dans votre implémentation, vous pouvez la fermer vous-même, si l'itérateur est épuisé.

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

Et clairement document:

cet itérateur va invoquer close () quand hasNext () retourne false, si vous devez disposer de l'itérateur avant de vous assurer que vous appelez close your self

exemple:

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

Btw, vous pouvez pendant que vous êtes là, vous pouvez mettre en œuvre Iterable et utiliser le amélioré pour boucle.

14
répondu OscarRyz 2010-07-15 17:14:22

créer un itérateur personnalisé qui implémente le interface Autoclosable

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

puis utilisez cet itérateur dans un essayez avec déclaration de ressources.

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

Ce modèle va fermer la ressource sous-jacente quoi qu'il arrive: - après la déclaration complète - et même si une exception est levée

enfin, documentez clairement comment cet itérateur doit être utilisé.

Si vous ne voulez pas délègue les appels, utilise une stratégie de pression. par exemple. avec java 8 lambda:

dao.findAll(r -> process(r));
12
répondu Nicolas Labrot 2015-04-06 20:08:04

vous pouvez la fermer dans un finaliseur mais ça ne va pas vous donner le comportement que vous souhaitez. Votre finalizer est appelé seulement quand le collecteur d'ordures veut nettoyer votre objet, de sorte que votre ressource pourrait rester ouverte. Pire, si quelqu'un s'accroche à votre itérateur, il ne fermera jamais.

il y a une possibilité de fermer le flux sur le premier appel à hasNext() qui renvoie false. Ce n'est pas encore garanti de le faire puisque quelqu'un pourrait itérer seulement le premier element et ne vous en souciez plus jamais.

vraiment, je pense que vous aurez besoin de le gérer vous-même lorsque vous avez affaire à une bibliothèque externe. Vous allez faire ces appels aux méthodes qui utilisent le itérable, alors pourquoi ne pas le fermer vous-même quand vous avez terminé? La gestion des ressources n'est pas quelque chose que vous pouvez simplement imposer une bibliothèque externe qui ne savent pas mieux.

8
répondu NG. 2010-07-15 17:05:28

il suffit de définir votre propre sous-interface D'Iterator qui inclut une méthode close, et assurez-vous que vous utilisez cela à la place de la classe iterator régulière. Par exemple, créez cette interface:

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

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

Et puis une mise en œuvre pourrait ressembler à ceci:

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();
    }
};
4
répondu Jesse Barnum 2013-07-09 04:19:36

j'ai eu un problème similaire dans un de mes projets utilisant un itérateur comme un flux D'objets. Pour couvrir les fois où L'itérateur n'est pas entièrement consommé, j'ai également eu besoin d'une méthode close. Au départ, j'ai simplement étendu les interfaces Iterator et Closable, mais en creusant un peu plus, la déclaration try-with-resources introduite dans java 1.7, j'ai pensé que fourni une façon ordonnée de l'implémenter.

vous étendez les interfaces Iterator et Autoclosable, implémentez la méthode Close et utilisez L'Iterator dans un try-with-resources. Le Runtime appellera close pour vous une fois que L'itérateur sera hors de portée.

https://docs.oracle.com/javase/7/docs/api/java/lang/AutoCloseable.html

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

Par exemple

L'Interface: public interface MyIterator<E> extends Iterator<E>, AutoCloseable { }

Une mise en Œuvre de: public class MyIteratorImpl implements 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;
}

} `

Et un exemple d'utilisation avec le try-with-ressource:

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

    }
    return foundString;
}

} `

1
répondu Eurospoofer 2016-10-09 17:29:32

Si possible, enveloppez l'itérateur dans un Flux qui vous donnera l'accès à l' onClose méthode des flux. Ensuite, vous devriez déplacer votre logique étroite dans cette méthode et faire votre nettoyage là-bas.

Exemple:

StreamSupport.stream(Spliterators.spliteratorUnknownSize(
        new MyCustomIterator<T>(), 0), false).onClose(() -> {
    // close logic here
});
1
répondu smac89 2018-08-18 20:09:49