Android Cursor को पुन: व्यवस्थित करने का सबसे अच्छा तरीका क्या है?


291

मैं अक्सर कोड देखता हूं जिसमें डेटाबेस क्वेरी के परिणाम पर पुनरावृत्ति शामिल है, प्रत्येक पंक्ति के साथ कुछ कर रहा है, और फिर अगली पंक्ति पर आगे बढ़ रहा है। विशिष्ट उदाहरण इस प्रकार हैं।

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तरीके हैं। निश्चित रूप से वहाँ एक रास्ता होना चाहिए?


1
इसका उद्देश्य क्या था? आपने इसे पोस्ट करने के एक मिनट के भीतर ही इसका जवाब दे दिया ...
बराक

10
मैंने इसका जवाब उसी समय दिया, जब यह पूछ रहा था।
ग्राहम बोरलैंड

1
आह, पहले कभी उस लिंक को नहीं देखा। यह सिर्फ एक सवाल पूछने के लिए मूर्खतापूर्ण लग रहा था आप जाहिरा तौर पर पहले से ही एक जवाब था।
बराक

5
@Barak: मुझे लगता है कि यह बहुत अच्छा है कि उसने पोस्ट डाल दिया - अब मुझे कुछ करने का एक छोटा सा तरीका पता है, जिसे मैं अन्यथा नहीं जानता।
जॉर्ज

4
मुझे यह स्पष्ट प्रतीत होता है कि आपने यह पोस्ट किसी ऐसे व्यक्ति की मदद करने के लिए किया है जो शायद देख रहा हो। इसके लिए आपको सहारा देता है, और सहायक टिप के लिए धन्यवाद!
muttley91

जवाबों:


515

सबसे सरल तरीका यह है:

while (cursor.moveToNext()) {
    ...
}

कर्सर पहली परिणाम पंक्ति से पहले शुरू होता है , इसलिए पहले पुनरावृत्ति पर यह मौजूद होने पर पहले परिणाम पर जाता है । यदि कर्सर खाली है, या अंतिम पंक्ति पहले ही संसाधित हो गई है, तो लूप बड़े करीने से बाहर निकलता है।

बेशक, एक बार जब आप इसके साथ कर लेते हैं तो कर्सर को बंद करना न भूलें, अधिमानतः एक finallyखंड में।

Cursor cursor = db.rawQuery(...);
try {
    while (cursor.moveToNext()) {
        ...
    }
} finally {
    cursor.close();
}

यदि आप API 19+ को लक्षित करते हैं, तो आप कोशिश-के-संसाधनों का उपयोग कर सकते हैं।

try (Cursor cursor = db.rawQuery(...)) {
    while (cursor.moveToNext()) {
        ...
    }
}

19
इसलिए यदि आप पहले से ही एक असामान्य स्थिति में कर्सर के साथ इस पुनरावृत्ति को करना चाहते थे, तो आप लूप से पहले कर्सर.moveToPosition (-1) का उपयोग करेंगे?
सैम

43
इसे बंद करने के लिए मत भूलना!
सिमोन

13
SQLite डेटाबेस क्वेरी कभी भी रिक्त नहीं होगी। यदि कोई परिणाम नहीं मिला तो यह एक खाली कर्सर लौटा देगा। ContentProvider क्वेरीज़ कभी-कभी अशक्त हो सकती है।
ग्राहम बोरलैंड

8
बस कुछ सेंट जोड़ने के लिए ... अगर कर्सर पर डेटा को स्थानांतरित करने से पहले कर्सर की जाँच करें तो चेक न करें () इससे पहले कि आप कर्सर पर पुनरावृति करने जा रहे हैं - आप पहली प्रविष्टि खो देंगे
AAverin

47
यदि ए का उपयोग कर रहे हैं CursorLoader, तो सुनिश्चित करें कि आप cursor.moveToPosition(-1)पुनरावृत्ति से पहले कॉल करते हैं , क्योंकि लोडर कर्सर को फिर से उपयोग करता है जब स्क्रीन ओरिएंटेशन बदलता है। इस मुद्दे पर नज़र रखने में एक घंटा बिताया!
विक्की चिजवानी

111

एक कर्सर के माध्यम से जाने के लिए सबसे अच्छा दिखने वाला तरीका निम्नलिखित है:

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

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

कर्सर को बाद में बंद करना न भूलें

संपादित करें: दिए गए समाधान बहुत अच्छा है यदि आपको कभी किसी कर्सर को पुनरावृत्त करने की आवश्यकता होती है, जिसके लिए आप जिम्मेदार नहीं हैं। एक अच्छा उदाहरण होगा, यदि आप कर्सर को एक विधि में तर्क के रूप में ले रहे हैं, और आपको कर्सर की वर्तमान स्थिति के बारे में चिंता किए बिना, किसी दिए गए मूल्य के लिए कर्सर को स्कैन करने की आवश्यकता है।


8
तीन अलग-अलग तरीकों को क्यों कहते हैं, जब आप इसे सिर्फ एक के साथ कर सकते हैं? आपको क्यों लगता है कि बेहतर है?
ग्राहम बोरलैंड

11
यह जाने का सबसे सुरक्षित तरीका है यदि आप पहले से मौजूद कर्सर को पुनः लोड कर रहे हैं और यह सुनिश्चित करना चाहते हैं कि आपका चलना शुरू से शुरू हो।
माइकल एलेर्स स्मिथ

9
मैं मानता हूं कि यह सरल विकल्प की तुलना में स्पष्ट है। मैं आमतौर पर संक्षिप्तता के पक्ष में हूं। लूप के साथ एक समान भिन्नता - android.codota.com/scenarios/51891850da0a87eb5be3cc22/…
drorw

शाप देने वालों के साथ थोड़ा अधिक खेला है, मैंने अपना जवाब अपडेट कर दिया है। दिए गए कोड सुनिश्चित करें कि किसी कर्सर को पुनरावृत्त करने का सबसे कुशल तरीका नहीं है, लेकिन इसमें इसके उपयोग हैं। (एडिट देखें)
एलेक्स स्टाइल

@AlexStyl हाँ, यह वास्तव में करता है! इसने मेरी पवित्रता को बचा लिया!
एलेसेंड्रो

45

मैं सिर्फ एक तीसरा विकल्प बताना चाहूंगा जो कर्सर के शुरू होने की स्थिति में न हो तो भी काम करेगा:

if (cursor.moveToFirst()) {
    do {
        // do what you need with the cursor here
    } while (cursor.moveToNext());
}

1
एक निरर्थक जाँच है। आप स्वीकार किए गए समाधान में दिए गए सरल के साथ, अगर + डू-जबकि की जगह ले सकते हैं, जो सरल / अधिक पठनीय है।
mtk

6
@mtk नहीं, यह निरर्थक नहीं है, बात है कि - अगर कर्सर पुन: उपयोग किया जा रहा है, यह एक ऐसी स्थिति में हो सकता है, इसलिए स्पष्ट रूप से करने की जरूरत moveToFirst फोन
माइक फिर से गुजरना

यह केवल तभी उपयोगी है जब आपके पास लॉगिंग के साथ एक और बयान हो; अन्यथा ग्राहम बोरलैंड का जवाब अधिक संक्षिप्त है।
आरडीएस

5
जैसा कि विक्की चिजवानी की टिप्पणी बताती है, वास्तविक दुनिया में ग्राहम बोरलैंड का जवाब असुरक्षित है और इसकी आवश्यकता है moveToPosition(-1)। इसलिए दोनों उत्तर समान रूप से सहमत हैं क्योंकि दोनों में दो कर्सर कॉल हैं। मुझे लगता है कि इस जवाब ने बोरलैंड के जवाब को किनारे कर दिया क्योंकि उसे -1जादू की संख्या की आवश्यकता नहीं थी ।
बेंजामिन

मैं वास्तव में उस स्थिति में इसके उपयोगी के बारे में सोचता हूं जो आप जानना चाहते हैं कि कर्सर के पास कोई मूल्य नहीं था क्योंकि यह आसान है और यहां बयान में कुछ और जोड़ने के लिए समझ में आता है।
चाचम १५

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

यह एक बहुत अच्छा समाधान है, लेकिन यह इस तथ्य को छिपाता है कि आप Cursorलूप के प्रत्येक पुनरावृत्ति में उसी उदाहरण का उपयोग कर रहे हैं ।
npace

8

नीचे बेहतर तरीका हो सकता है:

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

उपरोक्त कोड यह सुनिश्चित करेगा कि यह पूरे पुनरावृत्ति से गुजरेगा और पहले और अंतिम चलना से बच नहीं जाएगा।


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

अच्छा और कॉम्पैक्ट कार्यान्वयन के लिए +1। बगिफ़िक्स: मुझे iterator()भी पुन : toVisit = cursor.getCount();उपयोग करना चाहिए class IterableCursor<T extends Cursor> implements Iterable<T>, Iterator<T> {...जो एक वर्ग प्राप्त करता है जो extends CursorWrapper implements MyInterfaceMyInterface डेटाबेस गुणों के लिए गेटर्स को परिभाषित करता है। इस तरह से मेरे पास कर्सर आधारित Iterator <MyInterface>
k3b

4

Do / जबकि समाधान अधिक सुरुचिपूर्ण है, लेकिन यदि आप ऊपर पोस्ट किए गए बस समाधान का उपयोग करते हैं, तो MoveToPosition (-1) के बिना आप पहले तत्व (कम से कम संपर्क क्वेरी पर) को याद करेंगे।

मैं सुझाव देता हूँ:

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

2
if (cursor.getCount() == 0)
  return;

cursor.moveToFirst();

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

cursor.close();

2

कर्सर इंटरफ़ेस है कि एक का प्रतिनिधित्व करता है 2-dimensionalकिसी भी डेटाबेस की तालिका।

जब आप SELECTस्टेटमेंट का उपयोग करके कुछ डेटा को पुनः प्राप्त करने का प्रयास करते हैं , तो डेटाबेस 1 पहली बार CURSOR ऑब्जेक्ट बनाएगा और उसका संदर्भ आपको लौटाएगा।

इस लौटे संदर्भ का सूचक 0 स्थान पर इंगित कर रहा है जिसे अन्यथा कर्सर के पहले स्थान के रूप में कहा जाता है, इसलिए जब आप कर्सर से डेटा प्राप्त करना चाहते हैं, तो आपको 1 रिकॉर्ड पर जाना होगा, इसलिए हमें इसका उपयोग करना होगा moveToFirst

जब आप कर्सरmoveToFirst() पर विधि लागू करते हैं , तो यह कर्सर पॉइंटर को 1 स्थान पर ले जाता है। अब आप 1 रिकॉर्ड में मौजूद डेटा तक पहुंच सकते हैं

देखने का सबसे अच्छा तरीका:

कर्सर कर्सर

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

0

प्रारंभ में कर्सर पहली पंक्ति के शो में नहीं होता है, जिसका उपयोग करके moveToNext()आप कर्सर को पुनरावृत्त कर सकते हैं जब रिकॉर्ड मौजूद नहीं होता है return false, तब तक, जब तक कि यह return true,

while (cursor.moveToNext()) {
    ...
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.