SparseArray के माध्यम से पुनरावृति कैसे करें?


311

वहाँ जावा SparseArray (Android के लिए) पर पुनरावृति करने के लिए एक रास्ता है? मैं sparsearrayआसानी से सूचकांक द्वारा मूल्यों को प्राप्त करता था। मुझे एक नहीं मिला।


30
वाह, पूरी तरह से अप्रकाशित वर्ग के बारे में बात करते हैं , शून्य संग्रह इंटरफेस के अनुरूप है ...

1
आप TreeMap<Integer, MyType>कुंजी द्वारा क्रम में पुनरावृति करने के लिए अनुमति देता है जो एक का उपयोग कर सकते हैं । जैसा कि कहा गया है, SparseArray को HashMap की तुलना में अधिक कुशल बनाया गया है, लेकिन यह पुनरावृत्ति की अनुमति नहीं देता है।
जॉन बी

2
यह बहुत संभावना नहीं है कि आपके द्वारा चुने गए नक्शे का प्रदर्शन आपके ऐप में अड़चन होने वाला है।
जेफरी बट्टमैन

3
@JeffreyBlattman का मतलब यह नहीं है कि हमें सही संरचना का उपयोग करने से बचना चाहिए जब यह स्पष्ट रूप से उपयुक्त हो।
ठंढा

1
@frostymarvelous का कहना है कि यह तेज़ है, इसका मतलब है कि शायद 10ms से कम की बचत हो। क्या ऐप की अनुदान योजना में 10ms प्रासंगिक है? क्या यह एक उप-इष्टतम इंटरफ़ेस का उपयोग करने के लायक है जिसे समझना और बनाए रखना कठिन है? मुझे उन बातों का जवाब नहीं पता है, लेकिन जवाब "बिल्कुल विरल उपयोग की परवाह किए बिना" नहीं है।
जेफरी ब्लाटमैन

जवाबों:


536

लगता है मुझे इसका हल मिल गया। मैंने keyAt(index)फ़ंक्शन को ठीक से नहीं देखा था ।

तो मैं कुछ इस तरह से जाऊँगा:

for(int i = 0; i < sparseArray.size(); i++) {
   int key = sparseArray.keyAt(i);
   // get the object by the key.
   Object obj = sparseArray.get(key);
}

25
प्रलेखन में कहा गया है कि "keyAt (int index) 0 में एक इंडेक्स को देखते हुए ... आकार () - 1, इंडेक्स की-वैल्यू मैपिंग से कुंजी देता है जो यह SparseArray स्टोर करता है।" इसलिए यह आपके द्वारा वर्णित मामले के लिए भी मेरे लिए ठीक काम करता है।
रुजाना

12
यह सरणी के पूर्व-आकार को बेहतर बनाने और लूप में निरंतर मूल्य का उपयोग करने के लिए बेहतर है।
दिमित्री ज़यत्सेव

25
यहाँ सीधे मान का उपयोग करना आसान नहीं होगा?
मिलान क्रस्टिक

34
यह लूप के अंदर भी काम करेगा:Object obj = sparseArray.valueAt(i);
फ्लोरियन

27
valueAt(i)तेजी से है get(key), क्योंकि valueAt(i)और keyAt(i)दोनों कर रहे हैं हे (1) , लेकिन get(key)है ओ (log2 एन) , तो मैं निश्चित रूप से हमेशा प्रयोग करेंगे valueAt
मैकी

180

यदि आप कुंजियों की परवाह नहीं करते हैं, तो valueAt(int)सीधे मूल्यों तक पहुंचने के लिए विरल सरणी के माध्यम से पुनरावृत्ति करते समय इसका उपयोग किया जा सकता है।

for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
    Object obj = sparseArray.valueAt(i);
}

7
यदि आपकी पुनरावृत्ति कुंजी के बारे में परवाह नहीं करती है, तो valueAt () उपयोगी (और स्वीकृत समाधान की तुलना में तेज़) का उपयोग करना है: एक विशिष्ट मूल्य की लूप काउंटिंग घटनाएँ।
Sogger

2
sparseArray.size()एक चर में ले लो ताकि यह size()हर बार फोन नहीं करेगा ।
प्रतीक बुटानी

4
यह एक चर के आकार () की नकल करने के लिए बेमानी है। यह जांचना आसान है कि क्या आप केवल आकार () पद्धति के कोड को देखते हैं। मैं समझ नहीं पा रहा हूं कि आपने ऐसी बातें सुझाने से पहले क्यों नहीं किया ... मुझे 20 साल पहले का एक समय याद है जहां हमारे पास सरल लिंक की गई सूची थी जो वास्तव में उनके आकार को गिनने के लिए थी जो आपने उनसे इसके लिए पूछी थी, लेकिन मुझे विश्वास नहीं हुआ इस तरह की चीजें अभी भी मौजूद हैं ...
अविश्वसनीय जन

क्या यह महत्वपूर्ण क्रम में होने की गारंटी है?
हुगहुगटोटल

18

Ooor आप बस अपना खुद का ListIterator बनाएँ:

public final class SparseArrayIterator<E> implements ListIterator<E> {

private final SparseArray<E> array;
private int cursor;
private boolean cursorNowhere;

/**
 * @param array
 *            to iterate over.
 * @return A ListIterator on the elements of the SparseArray. The elements
 *         are iterated in the same order as they occur in the SparseArray.
 *         {@link #nextIndex()} and {@link #previousIndex()} return a
 *         SparseArray key, not an index! To get the index, call
 *         {@link android.util.SparseArray#indexOfKey(int)}.
 */
public static <E> ListIterator<E> iterate(SparseArray<E> array) {
    return iterateAt(array, -1);
}

/**
 * @param array
 *            to iterate over.
 * @param key
 *            to start the iteration at. {@link android.util.SparseArray#indexOfKey(int)}
 *            < 0 results in the same call as {@link #iterate(android.util.SparseArray)}.
 * @return A ListIterator on the elements of the SparseArray. The elements
 *         are iterated in the same order as they occur in the SparseArray.
 *         {@link #nextIndex()} and {@link #previousIndex()} return a
 *         SparseArray key, not an index! To get the index, call
 *         {@link android.util.SparseArray#indexOfKey(int)}.
 */
public static <E> ListIterator<E> iterateAtKey(SparseArray<E> array, int key) {
    return iterateAt(array, array.indexOfKey(key));
}

/**
 * @param array
 *            to iterate over.
 * @param location
 *            to start the iteration at. Value < 0 results in the same call
 *            as {@link #iterate(android.util.SparseArray)}. Value >
 *            {@link android.util.SparseArray#size()} set to that size.
 * @return A ListIterator on the elements of the SparseArray. The elements
 *         are iterated in the same order as they occur in the SparseArray.
 *         {@link #nextIndex()} and {@link #previousIndex()} return a
 *         SparseArray key, not an index! To get the index, call
 *         {@link android.util.SparseArray#indexOfKey(int)}.
 */
public static <E> ListIterator<E> iterateAt(SparseArray<E> array, int location) {
    return new SparseArrayIterator<E>(array, location);
}

private SparseArrayIterator(SparseArray<E> array, int location) {
    this.array = array;
    if (location < 0) {
        cursor = -1;
        cursorNowhere = true;
    } else if (location < array.size()) {
        cursor = location;
        cursorNowhere = false;
    } else {
        cursor = array.size() - 1;
        cursorNowhere = true;
    }
}

@Override
public boolean hasNext() {
    return cursor < array.size() - 1;
}

@Override
public boolean hasPrevious() {
    return cursorNowhere && cursor >= 0 || cursor > 0;
}

@Override
public int nextIndex() {
    if (hasNext()) {
        return array.keyAt(cursor + 1);
    } else {
        throw new NoSuchElementException();
    }
}

@Override
public int previousIndex() {
    if (hasPrevious()) {
        if (cursorNowhere) {
            return array.keyAt(cursor);
        } else {
            return array.keyAt(cursor - 1);
        }
    } else {
        throw new NoSuchElementException();
    }
}

@Override
public E next() {
    if (hasNext()) {
        if (cursorNowhere) {
            cursorNowhere = false;
        }
        cursor++;
        return array.valueAt(cursor);
    } else {
        throw new NoSuchElementException();
    }
}

@Override
public E previous() {
    if (hasPrevious()) {
        if (cursorNowhere) {
            cursorNowhere = false;
        } else {
            cursor--;
        }
        return array.valueAt(cursor);
    } else {
        throw new NoSuchElementException();
    }
}

@Override
public void add(E object) {
    throw new UnsupportedOperationException();
}

@Override
public void remove() {
    if (!cursorNowhere) {
        array.remove(array.keyAt(cursor));
        cursorNowhere = true;
        cursor--;
    } else {
        throw new IllegalStateException();
    }
}

@Override
public void set(E object) {
    if (!cursorNowhere) {
        array.setValueAt(cursor, object);
    } else {
        throw new IllegalStateException();
    }
}
}

9
IMHO यह थोड़ा अधिक इंजीनियरिंग लगता है। यह भयानक tho
hrules6872

12

पाई के रूप में सरल। बस सुनिश्चित करें कि आप वास्तव में पाश प्रदर्शन करने से पहले सरणी आकार प्राप्त करते हैं।

for(int i = 0, arraySize= mySparseArray.size(); i < arraySize; i++) {
   Object obj = mySparseArray.get(/* int key = */ mySparseArray.keyAt(i));
}

उम्मीद है की यह मदद करेगा।


11

जो कोई भी कोटलिन का उपयोग कर रहा है, उसके लिए ईमानदारी से SparseArray पर पुनरावृति करने का सबसे आसान तरीका है: Anko या Android KTX से कोटलिन एक्सटेंशन का उपयोग करें ! (Android केटीएक्स को इंगित करने के लिए यज़ज़ेलो को श्रेय)

बस बुलाओ forEach { i, item -> }


हाँ, आप वास्तव में सही हैं। मेरे बुरे, मैंने टैग को देखा और सोचा कि कोटलिन यहाँ नहीं होना चाहिए। लेकिन अब एक दूसरा विचार होना कि यह उत्तर कोटलिन के लिए एक अच्छा संदर्भ है। हालांकि Anko का उपयोग करने के बजाय मैं android.github.io/android-ktx/core-ktx का उपयोग करने की सलाह दूंगा (यदि आप कृपया अपना उत्तर संपादित कर सकते हैं और Android- ktx जोड़ सकते हैं तो मैं इसे
बढ़ा दूंगा

@Yazazzello हे, मैं भी Android KTX, अच्छी बात के बारे में पता नहीं था!
0101100101

7

SparseArrayउपरोक्त लूपिंग के उपयोग से सभी तत्वों को हटाने के लिए Exception

इससे बचने के लिए सभी तत्वों को SparseArrayसामान्य छोरों का उपयोग करने से हटाने के लिए नीचे दिए गए कोड का पालन करें

private void getValues(){      
    for(int i=0; i<sparseArray.size(); i++){
          int key = sparseArray.keyAt(i);
          Log.d("Element at "+key, " is "+sparseArray.get(key));
          sparseArray.remove(key);
          i=-1;
    }
}

2
मैं = -1; आखिर में कुछ नहीं करता। इसके अलावा एक विधि भी है जिसे .clear()इष्ट बनाया जाना चाहिए।
पॉल Woitaschek

आप थोड़ी देर () के बजाय एक () लूप का उपयोग क्यों करेंगे? आप जो कुछ भी कर रहे हैं वह समझ में नहीं आता है
फिल ए

मुझे लगता है कि सैक्यूरिस i-=1;अब लापता तत्व के लिए खाता लिखना चाहता था । लेकिन यह पाश वापस लौटने के लिए बेहतर है: for(int i=sparseArray.size()-1; i>=0; i++){...; याwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
ths

"उपरोक्त लूपिंग" जैसे संदर्भों का कोई मतलब नहीं है।
अविश्वसनीय जन

मुझे लगा कि एक 'पुनरावृत्ति' का बिंदु सुरक्षित वस्तु निष्कासन था। मैंने Iterator वर्ग के किसी भी उदाहरण को sparseArrays के साथ नहीं देखा है जैसे कि hashmaps के लिए। यह सुरक्षित ऑब्जेक्ट हटाने को संबोधित करने के सबसे करीब है, मुझे उम्मीद है कि यह समवर्ती संशोधन अपवादों के बिना काम करता है।
Androidcoder

5

इसके लिए सरल Iterator<T>और Iterable<T>कार्यान्वयन है SparseArray<T>:

public class SparseArrayIterator<T> implements Iterator<T> {
    private final SparseArray<T> array;
    private int index;

    public SparseArrayIterator(SparseArray<T> array) {
        this.array = array;
    }

    @Override
    public boolean hasNext() {
        return array.size() > index;
    }

    @Override
    public T next() {
        return array.valueAt(index++);
    }

    @Override
    public void remove() {
        array.removeAt(index);
    }

}

public class SparseArrayIterable<T> implements Iterable<T> {
    private final SparseArray<T> sparseArray;

    public SparseArrayIterable(SparseArray<T> sparseArray) {
        this.sparseArray = sparseArray;
    }

    @Override
    public Iterator<T> iterator() {
        return new SparseArrayIterator<>(sparseArray);
    }
}

यदि आप न केवल एक मूल्य बल्कि एक कुंजी को पुनरावृत्त करना चाहते हैं:

public class SparseKeyValue<T> {
    private final int key;
    private final T value;

    public SparseKeyValue(int key, T value) {
        this.key = key;
        this.value = value;
    }

    public int getKey() {
        return key;
    }

    public T getValue() {
        return value;
    }
}

public class SparseArrayKeyValueIterator<T> implements Iterator<SparseKeyValue<T>> {
    private final SparseArray<T> array;
    private int index;

    public SparseArrayKeyValueIterator(SparseArray<T> array) {
        this.array = array;
    }

    @Override
    public boolean hasNext() {
        return array.size() > index;
    }

    @Override
    public SparseKeyValue<T> next() {
        SparseKeyValue<T> keyValue = new SparseKeyValue<>(array.keyAt(index), array.valueAt(index));
        index++;
        return keyValue;
    }

    @Override
    public void remove() {
        array.removeAt(index);
    }

}

public class SparseArrayKeyValueIterable<T> implements Iterable<SparseKeyValue<T>> {
    private final SparseArray<T> sparseArray;

    public SparseArrayKeyValueIterable(SparseArray<T> sparseArray) {
        this.sparseArray = sparseArray;
    }

    @Override
    public Iterator<SparseKeyValue<T>> iterator() {
        return new SparseArrayKeyValueIterator<T>(sparseArray);
    }
}

उपयोगिता विधियाँ बनाना उपयोगी है जो वापस आती हैं Iterable<T>और Iterable<SparseKeyValue<T>>:

public abstract class SparseArrayUtils {
    public static <T> Iterable<SparseKeyValue<T>> keyValueIterable(SparseArray<T> sparseArray) {
        return new SparseArrayKeyValueIterable<>(sparseArray);
    }

    public static <T> Iterable<T> iterable(SparseArray<T> sparseArray) {
        return new SparseArrayIterable<>(sparseArray);
    }
}

अब आप इसे दोहरा सकते हैं SparseArray<T>:

SparseArray<String> a = ...;

for (String s: SparseArrayUtils.iterable(a)) {
   // ...
}

for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
  // ...
}

4

यदि आप कोटलिन का उपयोग करते हैं, तो आप उदाहरण के लिए विस्तार कार्यों का उपयोग कर सकते हैं:

fun <T> LongSparseArray<T>.valuesIterator(): Iterator<T> {
    val nSize = this.size()
    return object : Iterator<T> {
        var i = 0
        override fun hasNext(): Boolean = i < nSize
        override fun next(): T = valueAt(i++)
    }
}

fun <T> LongSparseArray<T>.keysIterator(): Iterator<Long> {
    val nSize = this.size()
    return object : Iterator<Long> {
        var i = 0
        override fun hasNext(): Boolean = i < nSize
        override fun next(): Long = keyAt(i++)
    }
}

fun <T> LongSparseArray<T>.entriesIterator(): Iterator<Pair<Long, T>> {
    val nSize = this.size()
    return object : Iterator<Pair<Long, T>> {
        var i = 0
        override fun hasNext(): Boolean = i < nSize
        override fun next() = Pair(keyAt(i), valueAt(i++))
    }
}

आप चाहें तो एक सूची में भी बदल सकते हैं। उदाहरण:

sparseArray.keysIterator().asSequence().toList()

मुझे लगता है removeकि LongSparseArrayस्वयं (इट्रेटर पर नहीं) का उपयोग करके आइटम हटाना भी सुरक्षित हो सकता है , क्योंकि यह आरोही क्रम में है।


संपादित करें: संग्रह-केटीएक्स ( यहां उदाहरण ) का उपयोग करके एक आसान तरीका है । यह मेरे द्वारा लिखे गए, कार्यरूप में, उसी तरह से लागू किया गया है।

ग्रेड के लिए यह आवश्यक है:

implementation 'androidx.core:core-ktx:#'
implementation 'androidx.collection:collection-ktx:#'

यहाँ LongSparseArray के लिए उपयोग है:

    val sparse= LongSparseArray<String>()
    for (key in sparse.keyIterator()) {
    }
    for (value in sparse.valueIterator()) {
    }
    sparse.forEach { key, value -> 
    }

और उन लोगों के लिए जो जावा का उपयोग करते हैं, आप उदाहरण के लिए LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratorऔर LongSparseArrayKt.forEach, का उपयोग कर सकते हैं । अन्य मामलों के लिए भी।


-5

जवाब नहीं है क्योंकि SparseArrayयह प्रदान नहीं करता है। जैसा pstकि यह कहा गया है, यह बात कोई इंटरफेस प्रदान नहीं करती है।

आप उन 0 - size()मूल्यों से लूप कर सकते हैं और वापस आने वाले मूल्यों को छोड़ सकते हैं null, लेकिन यह इसके बारे में है।

जैसा कि मैंने अपनी टिप्पणी में कहा है, अगर आपको Mapइसके बजाय एक का उपयोग करने की आवश्यकता है SparseArray। उदाहरण के लिए, TreeMapकुंजी द्वारा क्रम में पुनरावृत्तियों का उपयोग करें ।

TreeMap<Integer, MyType>

-6

स्वीकृत उत्तर में कुछ छेद हैं। SparseArray की सुंदरता यह है कि यह इंडोल में अंतराल की अनुमति देता है। तो, हम एक SparseArray में, जैसे दो नक्शे हो सकता है ...

(0,true)
(250,true)

सूचना का आकार यहाँ होगा 2. यदि हम आकार पर पुनरावृति करते हैं, तो हम केवल 0 और अनुक्रमणिका में मैप किए गए मानों के लिए मान प्राप्त करेंगे। इसलिए 250 की कुंजी के साथ मैपिंग तक पहुँच नहीं है।

for(int i = 0; i < sparseArray.size(); i++) {
   int key = sparseArray.keyAt(i);
   // get the object by the key.
   Object obj = sparseArray.get(key);
}

ऐसा करने का सबसे अच्छा तरीका अपने डेटा सेट के आकार पर पुनरावृति करना है, फिर उन इंडोल को सरणी पर एक () के साथ जांचें। यहां एक एडेप्टर के साथ एक उदाहरण है जहां मैं आइटम को बैच हटाने की अनुमति दे रहा हूं।

for (int index = 0; index < mAdapter.getItemCount(); index++) {
     if (toDelete.get(index) == true) {
        long idOfItemToDelete = (allItems.get(index).getId());
        mDbManager.markItemForDeletion(idOfItemToDelete);
        }
    }

मुझे लगता है कि आदर्श रूप से SparseArray परिवार में एक getKeys () विधि होगी, लेकिन अफसोस ऐसा नहीं है।


4
आप गलत हैं - keyAtविधि nth कुंजी का मान लौटाती है (आपके उदाहरण में keyAt(1)वापस आ जाएगी 250), इस बात से भ्रमित नहीं होना चाहिए कि getकिस कुंजी द्वारा संदर्भित तत्व का मान लौटाता है।
इबोर्ब

मुझे यकीन नहीं है कि आपकी टिप्पणी में 'यह' क्या है। क्या आप स्वीकार कर रहे हैं कि आपका उत्तर गलत है, या आप कह रहे हैं कि मेरी टिप्पणी गलत है? यदि बाद में डेवलपर की
।android.com

17
मेरा जवाब गलत है, मैं इसे नहीं हटाऊंगा ताकि दूसरे भी सीख सकें।
टायलर Pfaff
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.