Comment itérer à travers SparseArray?

Existe-t-il un moyen d'itérer sur Java SparseArray (pour Android) ? J'ai utilisé sparsearray pour obtenir facilement des valeurs par index. Je ne pouvais pas en trouver un.

262
demandé sur halfer 2011-11-03 21:24:26

9 réponses

Il semble que j'ai trouvé la solution. Je n'avais pas correctement remarqué la fonction keyAt(index).

Donc je vais aller avec quelque chose comme ceci:

for(int i = 0; i < sparseArray.size(); i++) {
   int key = sparseArray.keyAt(i);
   // get the object by the key.
   Object obj = sparseArray.get(key);
}
474
répondu Ruzanna 2017-06-29 11:13:06

Si vous ne vous souciez pas des clés, alors valueAt(int) peut être utilisé lors de l'itération à travers le tableau clairsemé pour accéder directement aux valeurs.

for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
    Object obj = sparseArray.valueAt(i);
}
166
répondu Pogo Lin 2015-01-05 02:28:24

Ooor vous venez de créer votre propre 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();
    }
}
}
16
répondu 0101100101 2015-02-20 14:10:14

Pour supprimer tous les éléments de SparseArray en utilisant la boucle ci-dessus conduit à Exception.

Pour éviter cela, suivez le code ci-dessous pour supprimer tous les éléments de SparseArray en utilisant des boucles normales

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;
    }
}
7
répondu Sackurise 2016-01-23 11:08:01

Simple comme tarte. Assurez-vous simplement de récupérer la taille du tableau avant que effectue réellement la boucle.

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

J'espère que cela aide.

7
répondu Pascal 2018-02-20 15:29:30

Pour celui qui utilise Kotlin, honnêtement, le moyen le plus simple d'itérer sur un SparseArray est: Utilisez l'extension Kotlin de Ankoou Android KTX! (crédit à Yazazzello pour avoir souligné Android KTX)

Appelez Simplement forEach { i, item -> }

6
répondu 0101100101 2018-02-20 01:08:03

Voici des implémentations simples Iterator<T> et Iterable<T> pour 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);
    }
}

Si vous voulez itérer non seulement une valeur mais aussi une clé:

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

Il est utile de créer des méthodes utilitaires qui renvoient Iterable<T> et 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);
    }
}

Maintenant, vous pouvez itérer SparseArray<T>:

SparseArray<String> a = ...;

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

for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
  // ...
}
4
répondu mixel 2016-09-29 15:48:21

La réponse est non parce que SparseArray Ne la fournit pas. Comme pst le dit, cette chose ne fournit aucune interface.

Vous pouvez faire une boucle à partir de 0 - size() et ignorer les valeurs qui renvoient null, mais c'est à ce sujet.

, Comme je dis dans mon commentaire, si vous avez besoin d'itérer l'utilisation d'un Map au lieu de SparseArray. Par exemple, utilisez un TreeMap qui itère dans l'ordre par la clé.

TreeMap<Integer, MyType>
-5
répondu John B 2014-10-11 06:27:05

La réponse acceptée a quelques trous dedans. La beauté du SparseArray est qu'il permet des lacunes dans les indeces. Donc, nous pourrions avoir deux cartes comme ça, dans un SparseArray...

(0,true)
(250,true)

Notez que la taille ici serait 2. Si nous itérons sur la taille, nous n'obtiendrons que des valeurs pour les valeurs mappées à l'index 0 et à l'index 1. Donc, le mappage avec une clé de 250 n'est pas accessible.

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

, La meilleure façon de le faire est d'itérer sur la taille de votre ensemble de données, vérifiez les indices avec un get() sur le tableau. Voici un exemple avec un adaptateur où j'autorise la suppression par lots d'éléments.

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

Je pense idéalement que la famille SparseArray aurait une méthode getKeys (), mais hélas ce n'est pas le cas.

-6
répondu Tyler Pfaff 2015-06-17 01:44:42