java - method - iterator remove
Cerrando un java.util.Iterator (6)
El problema es la condición al final . A menudo iteramos sobre una colección completa o conjunto de datos, por lo que estamos en el final en este 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é.
Implementé un java.util.Iterator personalizado usando un recurso que debería liberarse al final usando un método close()
. Ese recurso podría ser java.sql.ResultSet , java.io.InputStream, etc ...
public interface CloseableIterator<T> extends Iterator<T>
{
public void close();
}
Es posible que algunas bibliotecas externas que utilizan este iterador no sepan que debe estar cerrado. p.ej:
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 hasNext () devuelve falso. Mi problema es cuando la iteración del ciclo se rompe antes de la última iteración, como se muestra en mi ejemplo.
En su implementación, podría cerrarlo usted mismo, si el iterador está agotado.
public boolean hasNext() {
....
if( !hasNext ) {
this.close();
}
return hasNext;
}
Y documentar claramente:
Este iterador invocará close () cuando hasNext () devuelva false, si necesita deshacerse del iterador antes de asegurarse de que llama cierra 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ías mientras estés allí podrías implementar Iterable y usar el bucle for mejorado.
Puede cerrarlo en un finalizador 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ás que gestionarlo tú mismo cuando trabajes con 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.
Simplemente defina su propia interfaz secundaria de Iterator que incluya un método de cierre, y asegúrese de usarla en lugar de la clase Iterator habitual. Por ejemplo, crea esta interfaz:
import java.io.Closeable;
import java.util.Iterator;
public interface CloseableIterator<T> extends Iterator<T>, Closeable {}
Y luego una implementación podría verse así:
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();
}
};
Cree un iterador personalizado que implemente la interfaz AutoCloseable
public interface CloseableIterator<T> extends Iterator<T>, AutoCloseable {
}
Y luego use este iterador en una declaración de intento con una declaración de recursos .
try(CloseableIterator iterator = dao.findAll()) {
while(iterator.hasNext()){
process(iterator.next());
}
}
Este patrón cerrará el recurso subyacente pase lo que pase: - después de completar la declaración - e incluso si se lanza una excepción
Finalmente, documente claramente cómo se debe usar este iterador.
Si no desea delegar las llamadas cercanas, use una estrategia de inserción. p.ej. con java 8 lambda:
dao.findAll(r -> process(r));
Tuve un problema similar en uno de mis proyectos al usar un iterador como un flujo 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 implementarla.
Extiende las interfaces Iterator y AutoCloseable, implementa el método Close y usa Iterator en try-with-resources. The Runtime lo 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 esto: `public class MyIteratorImpl 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 de usarlo con try -with-resource:
`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;
}
} `