¿Cuál es la mejor manera de iterar un cursor de Android?

Resuelto Graham Borland asked hace 54 años • 10 respuestas

Con frecuencia veo código que implica iterar sobre el resultado de una consulta de base de datos, hacer algo con cada fila y luego pasar a la siguiente fila. Los ejemplos típicos son los siguientes.

Cursor cursor = db.rawQuery(...);
cursor.moveToFirst();
while (cursor.isAfterLast() == false) 
{
    ...
    cursor.moveToNext();
}
Cursor cursor = db.rawQuery(...);
for (boolean hasItem = cursor.moveToFirst(); 
     hasItem; 
     hasItem = cursor.moveToNext()) {
    ...
}
Cursor cursor = db.rawQuery(...);
if (cursor.moveToFirst()) {
    do {
        ...                 
    } while (cursor.moveToNext());
}

Todos estos me parecen excesivamente prolijos, cada uno con múltiples llamadas a Cursormétodos. ¿Seguramente debe haber una manera más ordenada?

Graham Borland avatar Jan 01 '70 08:01 Graham Borland
Aceptado

La forma más sencilla es esta:

while (cursor.moveToNext()) {
    ...
}

El cursor comienza antes de la primera fila de resultados, por lo que en la primera iteración se mueve al primer resultado, si existe . Si el cursor está vacío o la última fila ya ha sido procesada, entonces el bucle sale ordenadamente.

Por supuesto, no olvides cerrar el cursor una vez que hayas terminado, preferiblemente en una finallycláusula.

Cursor cursor = db.rawQuery(...);
try {
    while (cursor.moveToNext()) {
        ...
    }
} finally {
    cursor.close();
}

Si su objetivo es API 19+, puede utilizar try-with-resources.

try (Cursor cursor = db.rawQuery(...)) {
    while (cursor.moveToNext()) {
        ...
    }
}
Graham Borland avatar May 23 '2012 16:05 Graham Borland

La forma más atractiva que he encontrado para pasar por un cursor es la siguiente:

Cursor cursor;
... //fill the cursor here

for (cursor.moveToFirst(); !cursor.isAfterLast(); cursor.moveToNext()) {
    // do what you need with the cursor here
}

No olvides cerrar el cursor después.

EDITAR: La solución proporcionada es excelente si alguna vez necesita iterar un cursor del que no es responsable. Un buen ejemplo sería si está tomando un cursor como argumento en un método y necesita escanear el cursor en busca de un valor determinado, sin tener que preocuparse por la posición actual del cursor.

Alex Styl avatar May 20 '2013 07:05 Alex Styl

Sólo me gustaría señalar una tercera alternativa que también funciona si el cursor no está en la posición inicial:

if (cursor.moveToFirst()) {
    do {
        // do what you need with the cursor here
    } while (cursor.moveToNext());
}
Jörg Eisfeld avatar Mar 14 '2014 23:03 Jörg Eisfeld

A continuación podría ser la mejor manera:

if (cursor.moveToFirst()) {
   while (!cursor.isAfterLast()) {
         //your code to implement
         cursor.moveToNext();
    }
}
cursor.close();

El código anterior aseguraría que pasaría por toda la iteración y no escaparía de la primera ni de la última iteración.

Pankaj avatar Jan 24 '2016 11:01 Pankaj

¿Qué tal usar el bucle foreach?

Cursor cursor;
for (Cursor c : CursorUtils.iterate(cursor)) {
    //c.doSth()
}

Sin embargo, mi versión de CursorUtils debería ser menos fea, pero cierra automáticamente el cursor:

public class CursorUtils {
public static Iterable<Cursor> iterate(Cursor cursor) {
    return new IterableWithObject<Cursor>(cursor) {
        @Override
        public Iterator<Cursor> iterator() {
            return new IteratorWithObject<Cursor>(t) {
                @Override
                public boolean hasNext() {
                    t.moveToNext();
                    if (t.isAfterLast()) {
                        t.close();
                        return false;
                    }
                    return true;
                }
                @Override
                public Cursor next() {
                    return t;
                }
                @Override
                public void remove() {
                    throw new UnsupportedOperationException("CursorUtils : remove : ");
                }
                @Override
                protected void onCreate() {
                    t.moveToPosition(-1);
                }
            };
        }
    };
}

private static abstract class IteratorWithObject<T> implements Iterator<T> {
    protected T t;
    public IteratorWithObject(T t) {
        this.t = t;
        this.onCreate();
    }
    protected abstract void onCreate();
}

private static abstract class IterableWithObject<T> implements Iterable<T> {
    protected T t;
    public IterableWithObject(T t) {
        this.t = t;
    }
}
}
aleksander.w1992 avatar Dec 05 '2014 03:12 aleksander.w1992