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