什么是迭代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方法调用。肯定有更整洁的方法吧?

142248 次浏览

最简单的方法是:

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

Do/While解决方案更优雅,但如果你只使用上面发布的While解决方案,没有movetopposition(-1),你将错过第一个元素(至少在Contact查询中)。

我建议:

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

如何使用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();
}
if (cursor.getCount() == 0)
return;


cursor.moveToFirst();


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


cursor.close();

下面可能是更好的方法:

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

上面的代码将确保它将通过整个迭代,而不会在第一次和最后一次迭代中逃脱。

最初游标不在第一行显示使用moveToNext()你可以迭代游标当记录不存在时,然后它return false,除非它return true

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

光标是表示任何数据库的2-dimensional表的接口。

当你试图使用SELECT语句检索一些数据时,数据库将首先创建一个游标对象并将其引用返回给你。

此返回引用的指针指向第0个位置,否则称为Cursor的第一个位置,因此当您想从游标检索数据时,必须首先移动到第一条记录,因此我们必须使用moveToFirst

当你在游标,上调用moveToFirst()方法时,它会将光标指针指向第一个位置。现在可以访问第一条记录中的数据了

最佳观赏方式:

鼠标光标

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