SparseArray ile nasıl tekrarlanır?


311

Java SparseArray üzerinde (Android için) tekrarlamanın bir yolu var mı? Kullandığım sparsearraykolaylıkla dizine göre değerlerini alır. Bir tane bulamadım.


30
Vay be, tamamen sevilmeyen bir sınıf hakkında konuşmak , ZERO toplama arayüzlerine uygundur ...

1
TreeMap<Integer, MyType>Anahtarla sırayla yinelemenize izin veren bir kullanabilirsiniz . Belirtildiği gibi, SparseArray bir HashMap'ten daha verimli olacak şekilde tasarlanmıştır, ancak yinelemeye izin vermez.
John B

2
seçtiğiniz harita impl performansının uygulamanızdaki darboğaz olması çok, çok olası değildir.
Jeffrey Blattman

3
@JeffreyBlattman, açıkça uygun olduğunda doğru yapıyı kullanmaktan kaçınmamız gerektiği anlamına gelmez.
ayaz

1
@frostymarvelous, TWICE kadar hızlı olduğunu söylüyor, muhtemelen 10ms'den daha az tasarruf anlamına geliyor. 10ms, uygulamanın daha büyük düzeninde alakalı mı? Anlaması ve bakımı daha zor olan alt optimal bir arayüz kullanmaya değer mi? Ben bu şeylerin cevabını bilmiyorum, ama cevap "kesinlikle ne olursa olsun seyrek dizi kullanın" değildir.
Jeffrey Blattman

Yanıtlar:


537

Çözümü buldum. keyAt(index)İşlevi düzgün bir şekilde fark etmemiştim .

Bu yüzden böyle bir şeyle gideceğim:

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
belgeler "keyAt (int index) 0 ... size () - 1 aralığında bir dizin verildiğinde, bu SparseArray öğesinin depoladığı dizin anahtarı değer eşlemesinden anahtarı döndürür." bu yüzden sizin tarif ettiğiniz durumda bile benim için iyi çalışıyor.
Ruzanna

12
dizi boyutunu önceden hesaplamak ve döngüde sabit bir değer kullanmak daha iyidir.
Dmitry Zaytsev

25
Burada doğrudan valueAt işlevini kullanmak daha kolay olmaz mıydı?
Milan Krstic

34
Bu döngü içinde de işe yarayacaktır:Object obj = sparseArray.valueAt(i);
Florian

27
valueAt(i)daha hızlı olduğu get(key)için, valueAt(i)ve keyAt(i)her ikisi de O (1) , ama get(key)olan O (log 2 n) Kesinlikle hep kullanırım bu yüzden, valueAt.
Mecki

180

Tuşları umursamıyorsanız valueAt(int), değerlere doğrudan erişmek için seyrek dizi üzerinden yinelenirken kullanılabilir.

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

7
Yinelemeniz anahtarları umursamıyorsa valueAt () yöntemini kullanmak faydalıdır (ve kabul edilenden daha hızlı bir çözüm), yani: belirli bir değerin döngü sayımı oluşumlarını.
Sogger

2
sparseArray.size()Bir değişkeni alınız , böylece size()her seferinde çağrılamaz .
Pratik Butani

4
Size () değişkenini kopyalamak gereksizdir. Size () yönteminin koduna bakıp bakmadığınızı kontrol etmek kolaydır. Neden böyle şeyler önermeden önce anlamadığınızı anlayamıyorum ... 20 yıl önce, her istediğinde boyutlarını gerçekten saymak zorunda kalan basit bağlantılı listelerimizin olduğu bir zamanı hatırlıyorum ama inanmıyorum böyle şeyler hala var ...
İnanılmaz Ocak

Bunun kilit sırada olması garanti ediliyor mu?
HughHughTeotl

18

Kendi ListIterator'ınızı oluşturursunuz:

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 biraz fazla mühendislik gibi görünüyor. It's awesome tho
hrules6872

12

Pasta kadar basit. Sadece döngüyü gerçekleştirmeden önce dizi boyutunu getirdiğinizden emin olun .

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

Bu yardımcı olur umarım.


11

Kotlin'i kim kullanıyorsa, bir SparseArray üzerinde yinelemenin en kolay yolu şudur: Anko veya Android KTX'ten Kotlin uzantısını kullanın ! (Android KTX'i işaret ettiği için Yazazzello'ya teşekkür ederiz)

Sadece arayın forEach { i, item -> }


evet, aslında haklısın. kötüyüm, etiketlere baktım ve Kotlin'in burada olmaması gerektiğini düşündüm. Ama şimdi bu cevabın Kotlin'in kendisine iyi bir referans olduğu konusunda ikinci bir düşünceye sahip olmak. Her ne kadar Anko kullanmak yerine android.github.io/android-ktx/core-ktx kullanmanızı tavsiye ederim (eğer cevabınızı düzenleyebilir ve android-ktx ekleyebiliyorsanız, onu oylayacağım)
Yazazzello

@Yazazzello hey Android KTX'i bile bilmiyordum, iyi bir nokta!
0101100101

7

SparseArrayYukarıdaki döngüleri kullanmaktan tüm elemanları kaldırmak içinException .

Bunu önlemek SparseArrayiçin Normal döngülerden tüm öğeleri kaldırmak için aşağıdaki kodu izleyin

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; sonunda hiçbir şey yapmaz. Ayrıca .clear()tercih edilmesi gereken bir yöntem var .
Paul Woitaschek

Neden bir süre () yerine bir for () döngüsü kullanasınız? Yaptığınız şey döngü için bir anlam ifade etmiyor
Phil A

Sackurise i-=1;şimdi eksik öğe hesabına yazmak istedim varsayalım . Ancak döngüyü geri döndürmek daha iyidir for(int i=sparseArray.size()-1; i>=0; i++){...:; veyawhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
ths

"Yukarıdaki döngü" gibi referanslar hiç mantıklı değil.
İnanılmaz Ocak

Bir 'yineleyici' noktasının güvenli nesne kaldırma olduğunu düşündüm. Hasteps için olduğu gibi sparseArrays ile Iterator sınıfının herhangi bir örneğini görmedim. Bu güvenli nesne kaldırma ele en yakın geliyor, umarım eşzamanlı değişiklik istisnaları olmadan çalışır.
Androidcoder

5

İşte basit Iterator<T>ve Iterable<T>uygulamalar içinSparseArray<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);
    }
}

Sadece bir değeri değil aynı zamanda bir anahtarı da yinelemek istiyorsanız:

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

O dönüşü yarar yöntemlerinin oluşturmak kullanışlı olur Iterable<T>ve 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);
    }
}

Şimdi tekrarlayabilirsiniz SparseArray<T>:

SparseArray<String> a = ...;

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

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

4

Kotlin kullanıyorsanız, uzantı işlevlerini aşağıdaki gibi kullanabilirsiniz, örneğin:

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++))
    }
}

İsterseniz bir listeye de dönüştürebilirsiniz. Misal:

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

Ben bile kullanarak silme öğelere güvenli olabileceğini düşünüyorum removeüzerinde LongSparseArrayartan düzende olduğu gibi, (değil yineleyici üzerine) kendisi.


EDIT: collection-ktx kullanarak daha kolay bir yolu var gibi görünüyor (örnek burada ). Yazdıklarıma, benzer şekilde çok benzer bir şekilde uygulandı.

Gradle şunları gerektirir:

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

İşte LongSparseArray kullanımı:

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

Java kullananlar için, örneğin LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratorve kullanabilirsiniz LongSparseArrayKt.forEach. Diğer durumlar için de aynı.


-5

Cevap hayır çünkü SparseArraybunu sağlamaz. As pstkoymak, bu şey hiçbir arabirimleri sağlamaz.

Sen den döngü olabilir 0 - size()ve karşılığında o değerleri atlamak null, ancak bu konuda olduğunu.

Yorumumda belirttiğim gibi, yinelemeye ihtiyacınız varsa bir Mapyerine kullanın SparseArray. Örneğin TreeMap, tuş tarafından sırayla yinelenen bir kullanın .

TreeMap<Integer, MyType>

-6

Kabul edilen cevapta bazı delikler var. SparseArray'ın güzelliği, girintilerde boşluklara izin vermesidir. Bir SparseArray'de iki haritamız olabilirdi ...

(0,true)
(250,true)

Buradaki boyutun 2 olduğuna dikkat edin. Boyut üzerinden yinelersek, yalnızca dizin 0 ve dizin 1 ile eşlenen değerler için değerler alırız. Bu nedenle, 250 anahtarıyla eşleştirmeye erişilmez.

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

Bunu yapmanın en iyi yolu, veri kümenizin boyutu üzerinde yineleme yapmak, ardından dizindeki bir get () ile bu girintileri kontrol etmektir. Burada, öğelerin toplu olarak silinmesine izin verdiğim bir adaptörle bir örnek var.

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

İdeal olarak SparseArray ailesi getKeys () yöntemi olurdu, ama ne yazık ki yok.


4
Yanılıyorsunuz - keyAtyöntem nth anahtarının değerini döndürür (örneğinizde keyAt(1)dönecektir 250), karıştırılmaması için get, anahtar tarafından başvurulan öğenin değerini döndürür.
Eborbob

Yorumunuzda 'bunun' ne olduğundan emin değilim. Cevabınızın yanlış olduğunu mu söylüyorsunuz, yoksa yorumumun yanlış olduğunu mu söylüyorsunuz? İkincisi lütfen developer.android.com/reference/android/util/…
Eborbob

17
Cevabım yanlış, başkalarının öğrenebilmesi için silmeyeceğim.
Tyler Pfaff
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.