web-dev-qa-db-fra.com

Quel est le meilleur moyen d'itérer un curseur Android?

Je vois fréquemment du code qui implique une itération sur le résultat d'une requête de base de données, une opération à chaque ligne, puis le passage à la ligne suivante. Des exemples typiques sont les suivants.

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

Celles-ci me paraissent excessivement longues, chacune avec plusieurs appels aux méthodes Cursor. Il doit sûrement y avoir un chemin plus propre?

282
Graham Borland

La manière la plus simple est la suivante:

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

Le curseur commence avant la première ligne de résultat. Ainsi, lors de la première itération, le premier résultat s'il existe. Si le curseur est vide ou si la dernière ligne a déjà été traitée, la boucle se termine proprement.

Bien sûr, n'oubliez pas de fermer le curseur une fois que vous en avez terminé, de préférence dans une clause finally.

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

Si vous ciblez l'API 19+, vous pouvez utiliser try-with-resources.

try (Cursor cursor = db.rawQuery(...)) {
    while (cursor.moveToNext()) {
        ...
    }
}
507
Graham Borland

La meilleure façon de parcourir un curseur que j'ai trouvée est la suivante:

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

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

N'oubliez pas de fermer le curseur ensuite

EDIT: La solution proposée est excellente si vous avez besoin d'itérer un curseur dont vous n'êtes pas responsable. Un bon exemple serait, si vous prenez un curseur comme argument dans une méthode et que vous ayez besoin de scanner le curseur pour une valeur donnée, sans avoir à vous soucier de la position actuelle du curseur.

106
Alex Styl

Je voudrais juste signaler une troisième alternative qui fonctionne aussi si le curseur n'est pas à la position de départ:

if (cursor.moveToFirst()) {
    do {
        // do what you need with the cursor here
    } while (cursor.moveToNext());
}
43
Jörg Eisfeld

Que diriez-vous d'utiliser la boucle foreach:

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

Cependant, ma version de CursorUtils devrait être moins laide, mais elle ferme automatiquement le curseur:

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;
    }
}
}
9
aleksander.w1992

Ci-dessous pourrait être le meilleur moyen:

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

Le code ci-dessus garantirait qu'il passerait par une itération complète et n'échapperait pas à la première et à la dernière itération.

8
Pankaj
import Java.util.Iterator;
import Android.database.Cursor;

public class IterableCursor implements Iterable<Cursor>, Iterator<Cursor> {
    Cursor cursor;
    int toVisit;
    public IterableCursor(Cursor cursor) {
        this.cursor = cursor;
        toVisit = cursor.getCount();
    }
    public Iterator<Cursor> iterator() {
        cursor.moveToPosition(-1);
        return this;
    }
    public boolean hasNext() {
        return toVisit>0;
    }
    public Cursor next() {
    //  if (!hasNext()) {
    //      throw new NoSuchElementException();
    //  }
        cursor.moveToNext();
        toVisit--;
        return cursor;
    }
    public void remove() {
        throw new UnsupportedOperationException();
    }
}

Exemple de code:

static void listAllPhones(Context context) {
    Cursor phones = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
    for (Cursor phone : new IterableCursor(phones)) {
        String name = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME));
        String phoneNumber = phone.getString(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER));
        Log.d("name=" + name + " phoneNumber=" + phoneNumber);
    }
    phones.close();
}
5
18446744073709551615

La solution Do/While est plus élégante, mais si vous utilisez uniquement la solution While indiquée ci-dessus, sans moveToPosition (-1), vous manquerez le premier élément (au moins dans la requête Contact).

Je suggère:

if (cursor.getCount() > 0) {
    cursor.moveToPosition(-1);
    while (cursor.moveToNext()) {
          <do stuff>
    }
}
4
Lars
if (cursor.getCount() == 0)
  return;

cursor.moveToFirst();

while (!cursor.isAfterLast())
{
  // do something
  cursor.moveToNext();
}

cursor.close();
2
susemi99

Le curseur est l'interface qui représente une table 2-dimensional de toute base de données.

Lorsque vous essayez d'extraire des données à l'aide de l'instruction SELECT, la base de données crée alors un objet CURSOR et vous renvoie sa référence.

Le pointeur de cette référence renvoyée pointe vers le th location, qui est également appelé comme avant le premier emplacement du curseur. Ainsi, lorsque vous souhaitez récupérer des données à partir du curseur, vous devez d'abord vous déplacer vers le 1er enregistrement il faut donc utiliser moveToFirst

Lorsque vous appelez la méthode moveToFirst() sur le Cursor,, le pointeur du curseur se positionne sur le premier emplacement. Maintenant, vous pouvez accéder aux données présentes dans le 1er enregistrement

La meilleure façon de regarder:

Curseur curseur

for (cursor.moveToFirst(); 
     !cursor.isAfterLast();  
     cursor.moveToNext()) {
                  .........
     }
0
Rajshah