什么是迭代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()) { ... }