301

我经常看到涉及迭代数据库查询结果的代码,对每一行做一些事情,然后移动到下一行。典型例子如下。

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方法进行了多次调用。当然必须有一个更整洁的方法?

4

10 回答 10

527

最简单的方法是这样的:

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()) {
        ...
    }
}
于 2012-05-23T16:03:33.520 回答
120

我发现通过光标的最佳方式如下:

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

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

之后不要忘记关闭光标

编辑:如果您需要迭代您不负责的游标,给定的解决方案非常好。一个很好的例子是,如果您将游标作为方法中的参数,并且您需要扫描游标以查找给定值,而不必担心游标的当前位置。

于 2013-05-20T07:00:24.180 回答
48

我只想指出第三种选择,如果光标不在起始位置,它也可以工作:

if (cursor.moveToFirst()) {
    do {
        // do what you need with the cursor here
    } while (cursor.moveToNext());
}
于 2014-03-14T23:08:26.067 回答
10

下面可能是更好的方法:

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

上面的代码将确保它会经历整个迭代并且不会逃脱第一次和最后一次迭代。

于 2016-01-24T11:38:05.747 回答
9

如何使用 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;
    }
}
}
于 2014-12-05T03:24:35.857 回答
6
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();
}
于 2015-02-27T13:01:47.127 回答
4

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

我建议:

if (cursor.getCount() > 0) {
    cursor.moveToPosition(-1);
    while (cursor.moveToNext()) {
          <do stuff>
    }
}
于 2014-05-12T20:41:03.817 回答
3

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

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

这个返回引用的指针指向第 0 个位置,否则在游标的第一个位置之前被称为,所以当你想从游标中检索数据时,你必须第一次移动到第一个记录,所以我们必须使用移至第一

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

最好的观察方式:

光标光标

for (cursor.moveToFirst(); 
     !cursor.isAfterLast();  
     cursor.moveToNext()) {
                  .........
     }
于 2019-06-06T05:29:58.950 回答
2
if (cursor.getCount() == 0)
  return;

cursor.moveToFirst();

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

cursor.close();
于 2015-07-03T10:19:29.700 回答
0

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

while (cursor.moveToNext()) {
    ...
}
于 2016-06-02T07:36:24.737 回答