Quelle est la meilleure façon d’itérer un curseur Android?

Je vois souvent du code qui consiste à parcourir le résultat d’une requête de firebase database, à faire quelque chose avec chaque ligne, puis à passer à 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()); } 

Tout cela me semble excessivement long, chacun avec des appels multiples aux méthodes Cursor . Il doit sûrement y avoir une manière plus nette?

La manière la plus simple est la suivante:

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

Le curseur commence avant la première ligne de résultat. Par conséquent, lors de la première itération, il se déplace au 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 ferme parfaitement.

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 API 19+, vous pouvez utiliser try-with-resources.

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

La meilleure façon de passer par un curseur 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 après

EDIT: La solution donnée est parfaite 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 devez scanner le curseur pour une valeur donnée, sans vous soucier de la position actuelle du curseur.

Je voudrais juste signaler une troisième alternative qui fonctionne également 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()); } 

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 iterate(Cursor cursor) { return new IterableWithObject(cursor) { @Override public Iterator iterator() { return new IteratorWithObject(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 implements Iterator { protected T t; public IteratorWithObject(T t) { this.t = t; this.onCreate(); } protected abstract void onCreate(); } private static abstract class IterableWithObject implements Iterable { protected T t; public IterableWithObject(T t) { this.t = t; } } } 

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 assurerait qu’il traverserait toute l’itération et n’échapperait pas à la première et dernière itération.

 import java.util.Iterator; import android.database.Cursor; public class IterableCursor implements Iterable, Iterator { Cursor cursor; int toVisit; public IterableCursor(Cursor cursor) { this.cursor = cursor; toVisit = cursor.getCount(); } public Iterator 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)) { Ssortingng name = phone.getSsortingng(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME)); Ssortingng phoneNumber = phone.getSsortingng(phone.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)); Log.d("name=" + name + " phoneNumber=" + phoneNumber); } phones.close(); } 

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

Je suggère:

 if (cursor.getCount() > 0) { cursor.moveToPosition(-1); while (cursor.moveToNext()) {  } } 
 if (cursor.getCount() == 0) return; cursor.moveToFirst(); while (!cursor.isAfterLast()) { // do something cursor.moveToNext(); } cursor.close(); 

Initialement, le curseur n’est pas sur la première ligne, mais en utilisant moveToNext() vous pouvez itérer le curseur quand l’enregistrement n’existe pas, il return false , à moins qu’il ne return true ,

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

Avant de savoir comment itérer sur un curseur, vous devez savoir ce qu’est un curseur en général ou dans Android.
Cursor in Android est une interface qui représente un ensemble de résultats renvoyé par une firebase database.

(En termes simples, un curseur est un tableau avec des lignes et des colonnes).

L’interface de curseur a des méthodes très utiles pour jouer. Par exemple, obtenir le nombre total de lignes et de colonnes. Vous pouvez interroger le nom de la colonne, Vous pouvez même interroger les valeurs stockées dans une ligne particulière. Une fois que vous savez que cette itération sur un curseur est juste une autre chose que vous auriez faite dans le passé.
Voici comment je répète généralement sur un curseur.
Obtenir le nombre total de lignes à l’aide de la fonction getCount () de l’interface Cursor

  int rowSize = cursor.getCount(); cursor.moveToFirst(); // resets the value to -1 for(int i=0;i