2010-07-15 11 views
25

Implementé un personalizado java.util.Iterator usando un recurso que debería liberarse al final usando un método close(). Ese recurso podría ser un java.sql.ResultSet, un java.io.InputStream etc ...Cerrando un java.util.Iterator

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

Algunas bibliotecas externas que utilizan este iterador puede no saber que debe estar cerrada. por ejemplo:

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

En ese caso, ¿hay alguna manera de cerrar este iterador?

Actualización: muchas gracias por las respuestas actuales. Daré un (+1) a todos. Ya cierro el iterador cuando tieneNext() devuelve falso. Mi problema es cuando la iteración del ciclo rompe antes de la última iteración como se muestra en mi ejemplo.

Respuesta

8

El problema es la condición al final. A menudo iteramos sobre una colección completa o conjunto de datos, por lo que estamos al final por el momento, no hay datos para leer.

Pero si establecemos un corte fuera del ciclo antes de llegar al Fin de los datos, el iterador no llegará a su fin y no se cerrará.

Una salida podría ser almacenar en caché el contenido de la fuente de datos en el iterador durante la construcción y cerrar el recurso. Por lo tanto, el iterador no funcionaría en el recurso abierto, sino en los datos almacenados en caché.

+1

obras para seguro, pero puede no ser muy eficiente de la memoria o el almacenamiento en caché puede llegar a ser una operación muy pesada. – Eric

8

Puede cerrarlo en un finalizer pero no le dará el comportamiento que desea. Su finalizador se invoca solo cuando el recolector de basura quiere limpiar su objeto, por lo que su recurso puede permanecer abierto. Peor aún, si alguien retiene su iterador, nunca se cerrará.

Existe la posibilidad de cerrar la transmisión en la primera llamada a hasNext() que devuelve falso. Todavía no está garantizado hacerlo, ya que alguien podría iterar solo el primer elemento y nunca volver a molestarse.

Realmente, creo que tendrá que administrarlo usted mismo cuando se trata de una biblioteca externa. Vas a hacer esas llamadas a los métodos que usan el iterable, así que ¿por qué no cerrarlo tú mismo cuando termines? La gestión de recursos no es algo que pueda imponerse en una biblioteca externa que no conoce mejor.

12

En su implementación puede cerrarlo usted mismo, si cuando el iterador está agotado.

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

Y claramente documento:

Este iterador invocará close() cuando hasNext() return false, si es necesario disponer el iterador antes asegúrese de llamar a cerrar su auto

ejemplo:

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

Por cierto, podría mientras esté allí podría implementar Iterable y usar el bucle for mejorado.

4

Simplemente defina su propia subinterfaz de Iterator que incluya un método de cierre, y asegúrese de utilizarla en lugar de la clase de iterador regular. Por ejemplo, cree esta interfaz:

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

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

Y a continuación, una implementación podría tener este aspecto:

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

Esto no responde en absoluto, porque no explica cómo hacer que esos módulos, que no saben nada sobre los iteradores cerrables, cierren un iterador que se puede cerrar. – ceving

+0

No entiendo lo que intentas decir. ¿Cómo obtendría un módulo externo para agregar algún comportamiento nuevo (como cerrar un iterador), sin cambiar el código fuente de ese módulo externo? Presumiblemente, el módulo externo no tiene ninguna razón para llamar close() prematuramente. –

+0

Usar el enfoque que describí le daría un iterador que podría usarse con módulos existentes (y no daría como resultado el llamado close() antes de finalizar), o con módulos bajo su control (que llamarían close() cuando corresponda) . ¿Cómo se puede mejorar eso? –

9

Crear un iterador que implementan la AutoCloseable interface

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

y luego usar este iterador en un try with resource statement.

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

Este patrón se cerrará el recurso subyacente pase lo que pase: - después de la declaración completa - e incluso si se lanza una excepción

Finalmente, el documento claramente cómo se debe utilizar este iterador.

Si no desea delegar las llamadas cercanas, utilice una estrategia de inserción. p.ej. con Java 8 lambda:

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

tuve un problema similar en uno de mis proyectos utilizando un iterador como una corriente de objetos. Para cubrir los tiempos en que el iterador no se consume por completo, también necesitaba un método cercano. Inicialmente simplemente extendí las interfaces Iterator y Closable, pero profundizando un poco más, la declaración try-with-resources presentada en java 1.7, pensé que proporcionaba una forma ordenada de implementarlo.

Extiende las interfaces Iterator y AutoCloseable, implementa el método Close y usa el Iterator en try-with-resources. The Runtime llamará por usted una vez que el iterador esté fuera del alcance.

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

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

Por ejemplo

La interfaz: public interface MyIterator<E> extends Iterator<E>, AutoCloseable { }

una implementación de la misma: ` MyIteratorImpl clase pública implementa 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; 
} 

} `

Y un ejemplo del uso con el intento -con recursos:

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

} `

0

Si es posible, ajuste el iterador en una secuencia, que le dará acceso al método onClose de transmisiones. Luego, debe mover su inicio de sesión cercano a ese método y realizar su limpieza allí.

Ejemplo:

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