Existe-t-il un moyen d'itérer sur Java SparseArray (pour Android)? J'obtenais sparsearrayfacilement des valeurs par index. Je n'ai pas pu en trouver un.
Vous pouvez utiliser un TreeMap<Integer, MyType>qui vous permettrait d'itérer dans l'ordre par clé. Comme indiqué, SparseArray est conçu pour être plus efficace qu'un HashMap, mais il ne permet pas l'itération.
John B
2
il est très, très peu probable que les performances de l'impl de carte que vous choisissez soient le goulot d'étranglement de votre application.
Jeffrey Blattman
3
@JeffreyBlattman ne signifie pas que nous devons éviter d'utiliser la bonne structure lorsqu'elle est clairement appropriée.
frostymarvelous
1
@frostymarvelous dit que c'est deux fois plus rapide, ce qui signifie probablement une économie de moins de 10 ms. 10 ms est-il pertinent dans le schéma plus grand de l'application? Vaut-il la peine d'utiliser une interface sous-optimale plus difficile à comprendre et à maintenir? Je ne connais pas la réponse à ces choses, mais la réponse n'est pas "absolument utiliser un tableau clairsemé indépendamment".
Jeffrey Blattman
Réponses:
537
Il semble que j'ai trouvé la solution. Je n'avais pas bien remarqué la keyAt(index)fonction.
Je vais donc aller avec quelque chose comme ça:
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 documentation indique que "keyAt (int index) Étant donné un index dans la plage 0 ... size () - 1, renvoie la clé du mappage de valeur-clé indexth que ce SparseArray stocke." donc cela fonctionne bien pour moi même pour le cas que vous décrivez.
Ruzanna
12
il est préférable de précalculer la taille du tableau et d'utiliser une valeur constante en boucle.
Dmitry Zaytsev
25
Ne serait-il pas plus facile d'utiliser directement la fonction valueAt ici?
Milan Krstic
34
Cela fonctionnerait aussi à l'intérieur de la boucle:Object obj = sparseArray.valueAt(i);
Florian
27
valueAt(i)est plus rapide que get(key), parce que valueAt(i)et keyAt(i)sont tous les deux O (1) , mais get(key)est O (log2 n) , donc j'utiliserais certainement toujours valueAt.
Mecki
180
Si vous ne vous souciez pas des clés, vous valueAt(int)pouvez les utiliser 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);}
L'utilisation de valueAt () est utile (et plus rapide que la solution acceptée) si votre itération ne se soucie pas des clés, c'est-à-dire: une boucle comptant les occurrences d'une valeur spécifique.
Sogger
2
Prenez sparseArray.size()une variable pour qu'elle n'appelle pas à size()chaque fois.
Pratik Butani,
4
Il est redondant de copier size () dans une variable. Facile à vérifier si vous regardez simplement le code de la méthode size (). Je ne comprends pas pourquoi vous ne l'avez pas fait avant de suggérer de telles choses ... Je me souviens, il y a 20 ans, où nous avions de simples listes chaînées qui devaient vraiment compter leur taille chaque fois que vous leur en demandiez mais je ne crois pas que de telles choses existent toujours ...
L'incroyable
Est-ce que cela est garanti dans l'ordre clé?
HughHughTeotl
18
Ooor vous venez de créer votre propre ListIterator:
publicfinalclassSparseArrayIterator<E>implementsListIterator<E>{privatefinalSparseArray<E> array;privateint cursor;privateboolean 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)}.
*/publicstatic<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)}.
*/publicstatic<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)}.
*/publicstatic<E>ListIterator<E> iterateAt(SparseArray<E> array,int location){returnnewSparseArrayIterator<E>(array, location);}privateSparseArrayIterator(SparseArray<E> array,int location){this.array = array;if(location <0){
cursor =-1;
cursorNowhere =true;}elseif(location < array.size()){
cursor = location;
cursorNowhere =false;}else{
cursor = array.size()-1;
cursorNowhere =true;}}@Overridepublicboolean hasNext(){return cursor < array.size()-1;}@Overridepublicboolean hasPrevious(){return cursorNowhere && cursor >=0|| cursor >0;}@Overridepublicint nextIndex(){if(hasNext()){return array.keyAt(cursor +1);}else{thrownewNoSuchElementException();}}@Overridepublicint previousIndex(){if(hasPrevious()){if(cursorNowhere){return array.keyAt(cursor);}else{return array.keyAt(cursor -1);}}else{thrownewNoSuchElementException();}}@Overridepublic E next(){if(hasNext()){if(cursorNowhere){
cursorNowhere =false;}
cursor++;return array.valueAt(cursor);}else{thrownewNoSuchElementException();}}@Overridepublic E previous(){if(hasPrevious()){if(cursorNowhere){
cursorNowhere =false;}else{
cursor--;}return array.valueAt(cursor);}else{thrownewNoSuchElementException();}}@Overridepublicvoid add(E object){thrownewUnsupportedOperationException();}@Overridepublicvoid remove(){if(!cursorNowhere){
array.remove(array.keyAt(cursor));
cursorNowhere =true;
cursor--;}else{thrownewIllegalStateException();}}@Overridepublicvoid set(E object){if(!cursorNowhere){
array.setValueAt(cursor, object);}else{thrownewIllegalStateException();}}}
Pour ceux qui utilisent Kotlin, honnêtement, la façon de loin la plus simple de parcourir un SparseArray est: Utilisez l'extension Kotlin d' Anko ou Android KTX ! (crédit à Yazazzello pour avoir souligné Android KTX)
oui, vous avez en fait raison. mon mauvais, j'ai regardé les étiquettes et j'ai pensé que Kotlin ne devrait pas être ici. Mais ayant maintenant une seconde pensée que cette réponse est une bonne référence à Kotlin lui-même. Bien qu'au lieu d'utiliser Anko, je recommande d'utiliser android.github.io/android-ktx/core-ktx (si vous pouviez bien vouloir modifier votre réponse et ajouter android-ktx, je le voterais positivement)
Yazazzello
@Yazazzello hé je ne connaissais même pas Android KTX, bon point!
0101100101
7
Pour supprimer tous les éléments de l' SparseArrayutilisation des boucles ci-dessus,Exception .
Pour éviter cela Suivez le code ci-dessous pour supprimer tous les éléments de l' SparseArrayutilisation des boucles normales
privatevoid 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;}}
Le i = -1; à la fin ne fait rien. Il existe également une méthode appelée .clear()qui devrait être privilégiée.
Paul Woitaschek
Pourquoi utiliseriez-vous une boucle for () au lieu d'un while ()? Ce que vous faites n'a aucun sens pour la boucle
Phil A
je suppose que Sackurise voulait écrire i-=1;pour tenir compte de l'élément maintenant manquant. Mais il vaut mieux inverser la boucle for(int i=sparseArray.size()-1; i>=0; i++){...:; ouwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
le
Des références comme "le bouclage ci-dessus" n'ont aucun sens.
L'incroyable
Je pensais que le but d'un «itérateur» était de retirer des objets en toute sécurité. Je n'ai vu aucun exemple de la classe Iterator avec sparseArrays comme il y en a pour les hashmaps. Cela se rapproche le plus de la suppression sûre des objets, j'espère que cela fonctionne sans exceptions de modification simultanées.
Androidcoder
5
Voici des implémentations simples Iterator<T>et Iterable<T>pour SparseArray<T>:
Si vous utilisez Kotlin, vous pouvez utiliser les fonctions d'extension en tant que telles, par exemple:
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++))}}
Vous pouvez également convertir en liste, si vous le souhaitez. Exemple:
sparseArray.keysIterator().asSequence().toList()
Je pense qu'il pourrait même être sûr de supprimer des éléments en utilisant removesur LongSparseArraylui - même (pas sur l'itérateur), car il est dans l'ordre croissant.
EDIT: Il semble qu'il existe un moyen encore plus simple, en utilisant collection-ktx (exemple ici ). Il est mis en œuvre de manière très similaire à ce que j'ai écrit, en fait.
val sparse=LongSparseArray<String>()for(key in sparse.keyIterator()){}for(value in sparse.valueIterator()){}
sparse.forEach { key, value ->}
Et pour ceux qui utilisent Java, vous pouvez utiliser LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratoret LongSparseArrayKt.forEach, par exemple. Idem pour les autres cas.
La réponse est non car SparseArrayne la fournit pas. Comme pstdit, cette chose ne fournit aucune interface.
Vous pouvez boucler 0 - size()et ignorer les valeurs qui retournentnull , mais c'est à peu près tout.
Comme je l'indique dans mon commentaire, si vous avez besoin d'itérer, utilisez un Mapau lieu d'un SparseArray. Par exemple, utilisez un TreeMapqui itère dans l'ordre par la clé.
La réponse acceptée comporte quelques trous. La beauté du SparseArray est qu'il permet des lacunes dans les indéces. Donc, nous pourrions avoir deux cartes comme ça, dans un SparseArray ...
(0,true)(250,true)
Notez que la taille serait ici de 2. Si nous itérons sur la taille, nous n'obtiendrons que les valeurs des valeurs mappées à l'index 0 et à l'index 1. Ainsi, 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, puis de vérifier ces indéces 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 que l'idéal serait que la famille SparseArray ait une méthode getKeys (), mais hélas non.
Vous avez tort - la keyAtméthode retourne la valeur de la nième clé (dans votre exemple keyAt(1)retournerait 250), à ne pas confondre avec celle getqui retourne la valeur de l'élément référencé par la clé.
Eborbob
Je ne suis pas sûr de savoir ce que cela signifie dans votre commentaire. Admettez-vous que votre réponse est fausse ou dites-vous que mon commentaire est faux? Si ce dernier, veuillez vérifier developer.android.com/reference/android/util/…
Eborbob
17
Ma réponse est fausse, je ne la supprimerai pas pour que les autres puissent apprendre.
TreeMap<Integer, MyType>
qui vous permettrait d'itérer dans l'ordre par clé. Comme indiqué, SparseArray est conçu pour être plus efficace qu'un HashMap, mais il ne permet pas l'itération.Réponses:
Il semble que j'ai trouvé la solution. Je n'avais pas bien remarqué la
keyAt(index)
fonction.Je vais donc aller avec quelque chose comme ça:
la source
Object obj = sparseArray.valueAt(i);
valueAt(i)
est plus rapide queget(key)
, parce quevalueAt(i)
etkeyAt(i)
sont tous les deux O (1) , maisget(key)
est O (log2 n) , donc j'utiliserais certainement toujoursvalueAt
.Si vous ne vous souciez pas des clés, vous
valueAt(int)
pouvez les utiliser lors de l'itération à travers le tableau clairsemé pour accéder directement aux valeurs.la source
sparseArray.size()
une variable pour qu'elle n'appelle pas àsize()
chaque fois.Ooor vous venez de créer votre propre ListIterator:
la source
Simple comme tarte. Assurez-vous simplement de récupérer la taille du tableau avant d' effectuer la boucle.
J'espère que cela t'aides.
la source
Pour ceux qui utilisent Kotlin, honnêtement, la façon de loin la plus simple de parcourir un SparseArray est: Utilisez l'extension Kotlin d' Anko ou Android KTX ! (crédit à Yazazzello pour avoir souligné Android KTX)
Appelez simplement
forEach { i, item -> }
la source
Pour supprimer tous les éléments de l'
SparseArray
utilisation des boucles ci-dessus,Exception
.Pour éviter cela Suivez le code ci-dessous pour supprimer tous les éléments de l'
SparseArray
utilisation des boucles normalesla source
.clear()
qui devrait être privilégiée.i-=1;
pour tenir compte de l'élément maintenant manquant. Mais il vaut mieux inverser la bouclefor(int i=sparseArray.size()-1; i>=0; i++){...
:; ouwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
Voici des implémentations simples
Iterator<T>
etIterable<T>
pourSparseArray<T>
:Si vous souhaitez itérer non seulement une valeur mais également une clé:
Il est utile de créer des méthodes utilitaires qui renvoient
Iterable<T>
etIterable<SparseKeyValue<T>>
:Vous pouvez maintenant répéter
SparseArray<T>
:la source
Si vous utilisez Kotlin, vous pouvez utiliser les fonctions d'extension en tant que telles, par exemple:
Vous pouvez également convertir en liste, si vous le souhaitez. Exemple:
Je pense qu'il pourrait même être sûr de supprimer des éléments en utilisant
remove
surLongSparseArray
lui - même (pas sur l'itérateur), car il est dans l'ordre croissant.EDIT: Il semble qu'il existe un moyen encore plus simple, en utilisant collection-ktx (exemple ici ). Il est mis en œuvre de manière très similaire à ce que j'ai écrit, en fait.
Gradle exige ceci:
Voici l'utilisation de LongSparseArray:
Et pour ceux qui utilisent Java, vous pouvez utiliser
LongSparseArrayKt.keyIterator
,LongSparseArrayKt.valueIterator
etLongSparseArrayKt.forEach
, par exemple. Idem pour les autres cas.la source
La réponse est non car
SparseArray
ne la fournit pas. Commepst
dit, cette chose ne fournit aucune interface.Vous pouvez boucler
0 - size()
et ignorer les valeurs qui retournentnull
, mais c'est à peu près tout.Comme je l'indique dans mon commentaire, si vous avez besoin d'itérer, utilisez un
Map
au lieu d'unSparseArray
. Par exemple, utilisez unTreeMap
qui itère dans l'ordre par la clé.la source
La réponse acceptée comporte quelques trous. La beauté du SparseArray est qu'il permet des lacunes dans les indéces. Donc, nous pourrions avoir deux cartes comme ça, dans un SparseArray ...
Notez que la taille serait ici de 2. Si nous itérons sur la taille, nous n'obtiendrons que les valeurs des valeurs mappées à l'index 0 et à l'index 1. Ainsi, le mappage avec une clé de 250 n'est pas accessible.
La meilleure façon de le faire est d'itérer sur la taille de votre ensemble de données, puis de vérifier ces indéces avec un get () sur le tableau. Voici un exemple avec un adaptateur où j'autorise la suppression par lots d'éléments.
Je pense que l'idéal serait que la famille SparseArray ait une méthode getKeys (), mais hélas non.
la source
keyAt
méthode retourne la valeur de la nième clé (dans votre exemplekeyAt(1)
retournerait250
), à ne pas confondre avec celleget
qui retourne la valeur de l'élément référencé par la clé.