什么是迭代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
方法的调用。 当然必须有一个更好的方法?
最简单的方法是这样的:
while (cursor.moveToNext()) { ... }
游标在第一个结果行之前开始,所以在第一次迭代时, 如果第一个结果存在,则它移到第一个结果。 如果游标是空的,或者最后一行已经被处理,那么循环就会整齐地退出。
当然,不要忘了在完成之后closures游标,最好是在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 }
不要忘记随后closures光标
编辑:给定的解决scheme是伟大的,如果你需要迭代游标,你不负责。 一个很好的例子是,如果你将一个游标作为方法中的参数,并且你需要扫描游标给定的值,而不必担心游标的当前位置。
我只想指出,如果光标不在起始位置,也可以使用第三种替代方法:
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版本应该不那么难看,但它会自动closures游标:
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.moveToFirst()) { while (!cursor.isAfterLast()) { //your code to implement cursor.moveToNext(); } } cursor.close();
上面的代码将确保它将贯穿整个迭代,并且不会逃脱第一次和最后一次迭代。
Do / While解决scheme更加优雅,但是如果仅使用上面发布的While解决scheme,而没有moveToPosition(-1),则会错过第一个元素(至less在联系人查询中)。
我build议:
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()
你可以迭代游标时,logging不存在然后它return false
,除非它return true
,
while (cursor.moveToNext()) { ... }