Что это лучший способ, чтобы выполнить итерации курсор Андроид?


Я часто вижу код, который включает в себя итерацию по результату запроса базы данных, что-то делать с каждой строкой, а затем перейти к следующей строке. Типичными примерами являются следующие.

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

все они кажутся мне чрезмерно многословными, каждый с несколькими вызовами Cursor методы. Конечно, должен быть более аккуратный способ?

10 261

10 ответов:

самый простой способ это:

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

курсор начинается до первая строка результата, поэтому на первой итерации это переходит к первому результату если он существует. Если курсор пуст или последняя строка уже обработана, то цикл завершается аккуратно.

конечно, не забудьте закрыть курсор, как только вы закончите с ним, желательно в finally предложения.

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

если вы нацелены на API 19+, Вы можете использовать попытки с ресурсами.

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

самый красивый способ, который я нашел, чтобы пройти через курсор, заключается в следующем:

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

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

Не забудьте закрыть курсор после

EDIT: данное решение отлично подходит, если вам когда-нибудь понадобится повторить курсор, за который вы не несете ответственности. Хорошим примером может быть, если вы принимаете курсор в качестве аргумента в методе, и вам нужно сканировать курсор на заданное значение, не беспокоясь о текущей позиции курсора.

Я просто хотел бы указать на третий вариант, который также работает, если курсор не находится в начальной позиции:

if (cursor.moveToFirst()) {
    do {
        // do what you need with the cursor here
    } while (cursor.moveToNext());
}

Как насчет использования цикла foreach:

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

однако моя версия CursorUtils должна быть менее уродливой, но она автоматически закрывает курсор:

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

ниже может быть лучше так:

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

приведенный выше код гарантирует, что он будет проходить через всю итерацию и не будет избегать первой и последней итерации.

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

пример кода:

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

решение Do / While более элегантно, но если вы используете только решение While, опубликованное выше, без moveToPosition(-1) вы пропустите первый элемент (по крайней мере, в запросе контакта).

Я предлагаю:

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

изначально курсор не находится на первой строке шоу с помощью moveToNext() вы можете повторить курсор, когда запись не существует, то это return false,если не return true,

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

прежде чем знать, как перебирать курсор, вы должны знать, что такое курсор вообще или в Android.
Курсор в Android-это интерфейс, который представляет собой результирующий набор, возвращаемый базой данных

(в терминах непрофессионалов курсор-это таблица со строками и столбцами).

интерфейс Курсора имеет довольно полезно методы, чтобы поиграть. Например, получение общего количества строк и столбцов. Вы можете запросить столбец имя, вы даже можете запросить значения, хранящиеся в определенной строке. Как только вы узнаете, что эта итерация над курсором-это просто еще одна программная вещь, которую вы бы сделали в прошлом.
Вот как вообще, как я перебираю курсор.
Получить общее количество строк с помощью функции getCount () интерфейса Курсора

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

   for(int i=0;i<rowSize;i++)
   {
        cursor.moveToNext();       // moves to next row   
        // Write your logic
   }

   cursor.close();                // close the cursor at the end