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?
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()) {
...
}
}
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.
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());
}
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;
}
}
}
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.
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();
}
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>
}
}
if (cursor.getCount() == 0)
return;
cursor.moveToFirst();
while (!cursor.isAfterLast())
{
// do something
cursor.moveToNext();
}
cursor.close();
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()) {
.........
}