我经常看到这样的代码:遍历数据库查询的结果,对每一行执行一些操作,然后移动到下一行。典型例子如下。

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


当前回答

游标是Interface,表示任何数据库的二维表。

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

这个返回引用的指针指向第0个位置,也就是光标的第一个位置,所以当你想从光标中检索数据时,你必须首先移动到第1条记录,所以我们必须使用moveToFirst

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

最佳观赏方式:

鼠标光标

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

其他回答

最简单的方法是:

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()) {
        ...
    }
}

如何使用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;
    }
}
}

游标是Interface,表示任何数据库的二维表。

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

这个返回引用的指针指向第0个位置,也就是光标的第一个位置,所以当你想从光标中检索数据时,你必须首先移动到第1条记录,所以我们必须使用moveToFirst

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

最佳观赏方式:

鼠标光标

for (cursor.moveToFirst(); 
     !cursor.isAfterLast();  
     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()) {
   while (!cursor.isAfterLast()) {
         //your code to implement
         cursor.moveToNext();
    }
}
cursor.close();

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