Какой лучший способ перебора курсора Android?
Я часто вижу код, который включает в себя итерацию по результату запроса к базе данных, выполнение чего-либо с каждой строкой, а затем переход к следующей строке. Типичные примеры следующие.
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
методы. Конечно, должен быть аккуратный путь?
9 ответов
Самый простой способ заключается в следующем:
while (cursor.moveToNext()) {
...
}
Курсор начинается перед первой строкой результата, поэтому на первой итерации он перемещается к первому результату, если он существует. Если курсор пуст или последняя строка уже обработана, цикл завершается аккуратно.
Конечно, не забудьте закрыть курсор, как только вы закончите, желательно в finally
пункт.
Cursor cursor = db.rawQuery(...);
try {
while (cursor.moveToNext()) {
...
}
} finally {
cursor.close();
}
Если вы нацелены на API 19+, вы можете использовать try-with-resources.
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
}
Не забудьте потом закрыть курсор
РЕДАКТИРОВАТЬ: Данное решение отлично подходит, если вам когда-либо потребуется перебрать курсор, за который вы не несете ответственности. Хорошим примером будет, если вы берете курсор в качестве аргумента в методе, и вам нужно сканировать курсор на предмет заданного значения, не беспокоясь о текущей позиции курсора.
Я просто хотел бы указать на третий вариант, который также работает, если курсор не находится в начальной позиции:
if (cursor.moveToFirst()) {
do {
// do what you need with the cursor here
} while (cursor.moveToNext());
}
Ниже может быть лучший способ:
if (cursor.moveToFirst()) {
while (!cursor.isAfterLast()) {
//your code to implement
cursor.moveToNext();
}
}
cursor.close();
Приведенный выше код гарантирует, что он пройдет всю итерацию и не избежит первой и последней итерации.
Как насчет использования цикла 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;
}
}
}
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) вы пропустите первый элемент (по крайней мере, в запросе Contact).
Я предлагаю:
if (cursor.getCount() > 0) {
cursor.moveToPosition(-1);
while (cursor.moveToNext()) {
<do stuff>
}
}
Курсор - это Интерфейс, который представляет 2-dimensional
таблица любой базы данных.
Когда вы пытаетесь получить некоторые данные, используя SELECT
оператора, база данных сначала создаст объект CURSOR и вернет его ссылку на вас.
Указатель этой возвращенной ссылки указывает на 0-ю позицию, которая в противном случае вызывается так же, как и до первой позиции Курсора, поэтому, когда вы хотите извлечь данные из курсора, вам нужно сначала перейти к 1-й записи, поэтому мы должны использовать moveToFirst
Когда вы вызываете moveToFirst()
метод на курсоре , он берет указатель курсора на 1-е место. Теперь вы можете получить доступ к данным, присутствующим в 1-й записи
Лучший способ выглядеть:
Курсор Курсор
for (cursor.moveToFirst();
!cursor.isAfterLast();
cursor.moveToNext()) {
.........
}
if (cursor.getCount() == 0)
return;
cursor.moveToFirst();
while (!cursor.isAfterLast())
{
// do something
cursor.moveToNext();
}
cursor.close();
Первоначально курсор находится не в первом ряду, используя moveToNext()
Вы можете перебрать курсор, когда запись не существует, то она return false
, если это return true
,
while (cursor.moveToNext()) {
...
}