web-dev-qa-db-ja.com

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メソッドを複数回呼び出します。きちんとした方法があるはずですか?

276
Graham Borland

最も簡単な方法はこれです:

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

私がカーソルを通過するために私が見つけた最も見栄えの良い方法は次のとおりです。

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

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

後でカーソルを閉じることを忘れないでください

EDIT:与えられた解決策は、自分が責任を持たないカーソルを繰り返す必要がある場合に最適です。たとえば、メソッドの引数としてカーソルを使用していて、カーソルの現在位置を気にせずにカーソルをスキャンして特定の値を取得する必要がある場合がその例です。

104
Alex Styl

カーソルが開始位置にない場合にも機能する3番目の選択肢を指摘しておきます。

if (cursor.moveToFirst()) {
    do {
        // do what you need with the cursor here
    } while (cursor.moveToNext());
}
43
Jörg Eisfeld

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;
    }
}
}
9

以下がより良い方法かもしれません:

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

上記のコードは、それが繰り返し全体を通過し、最初と最後の繰り返しから逃げることがないように保証します。

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

Do/Whileソリューションはより洗練されていますが、moveToPosition(-1)を使用せずに上記のWhileソリューションのみを使用すると、(少なくともContactクエリで)最初の要素が欠落します。

私は提案します:

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

cursor.moveToFirst();

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

cursor.close();
2
susemi99

最初は、moveToNext()を使用して最初の行にカーソルが表示されません。レコードが存在しない場合はカーソルを反復処理でき、return falseは、return trueでない限り、

while (cursor.moveToNext()) {
    ...
}
0
kundan kamal

cursorは、あらゆるデータベースの2-dimensionalテーブルを表すインタフェースです。

SELECT文を使用してデータを取得しようとすると、データベースは最初にCURSORオブジェクトを作成し、その参照を返します。

この返された参照のポインタは、カーソルの最初の位置の前と同じように呼び出される番目の位置を指しているため、カーソルからデータを取得するときは、最初に移動する必要があります。最初のレコードなので、moveToFirstを使用する必要があります

Cursor、に対してmoveToFirst()メソッドを呼び出すと、カーソルポインタが1番目の位置に移動します。これで、1番目のレコードにあるデータにアクセスできます

見ての最善の方法:

カーソルカーソル

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