Puede usar uno TreeMap<Integer, MyType>que le permita iterar en orden por clave. Como se indicó, SparseArray está diseñado para ser más eficiente que un HashMap, pero no permite la iteración.
John B
2
es muy, muy poco probable que el rendimiento del mapa implícito que elija sea el cuello de botella en su aplicación.
Jeffrey Blattman
3
@JeffreyBlattman no significa que debamos evitar usar la estructura correcta cuando sea claramente apropiada.
frostymarvelous
1
@frostymarvelous dice que es DOS VECES más rápido, eso probablemente signifique un ahorro de menos de 10 ms. ¿Son relevantes 10 ms en el esquema más amplio de la aplicación? ¿Vale la pena usar una interfaz subóptima que sea más difícil de entender y mantener? No sé la respuesta a esas cosas, pero la respuesta no es "absolutamente usar matriz dispersa independientemente".
Jeffrey Blattman
Respuestas:
537
Parece que encontré la solución. No había notado correctamente la keyAt(index)función.
Entonces iré con algo como esto:
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 documentación indica que "keyAt (int index) Dado un índice en el rango 0 ... size () - 1, devuelve la clave de la asignación de clave-valor indexth que almacena este SparseArray". así que funciona bien para mí, incluso para el caso descrito por usted.
Ruzanna
12
Es mejor calcular previamente el tamaño de la matriz y usar un valor constante en el bucle.
Dmitry Zaytsev
25
¿No sería más fácil usar directamente la función valueAt aquí?
Milan Krstic
34
Esto también funcionaría dentro del circuito:Object obj = sparseArray.valueAt(i);
Florian
27
valueAt(i)es más rápido que get(key), porque valueAt(i)y keyAt(i)son O (1) , pero get(key)es O (log2 n) , por lo que ciertamente siempre lo usaría valueAt.
Mecki
180
Si no le importan las claves, valueAt(int)puede utilizarlas mientras realiza una iteración a través de la matriz dispersa para acceder a los valores directamente.
for(int i =0, nsize = sparseArray.size(); i < nsize; i++){Object obj = sparseArray.valueAt(i);}
El uso de valueAt () es útil (y más rápido que la solución aceptada) si su iteración no se preocupa por las claves, es decir: ocurre un recuento de bucles de un valor específico.
Sogger
2
Tome sparseArray.size()una variable para que no llame size()siempre.
Pratik Butani
44
Es redundante copiar size () a una variable. Fácil de verificar si solo mira el código del método size (). No puedo entender por qué no lo hiciste antes de sugerir esas cosas ... Recuerdo un momento hace 20 años en el que teníamos listas simples vinculadas que realmente tenían que contar su tamaño cada vez que las pedías, pero no creo que tales cosas todavía existen ...
El increíble Jan
¿Se garantiza que esto esté en el orden clave?
HughHughTeotl
18
O bien, simplemente cree su propio 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();}}}
Para quien esté usando Kotlin, honestamente, la forma más fácil de iterar sobre un SparseArray es: ¡Use la extensión Kotlin de Anko o Android KTX ! (crédito a Yazazzello por señalar Android KTX)
Sí, en realidad tienes razón. mal, miré las etiquetas y pensé que Kotlin no debería estar aquí. Pero ahora teniendo un segundo pensamiento que esta respuesta es una buena referencia a Kotlin en sí. Aunque en lugar de usar Anko, recomendaría usar android.github.io/android-ktx/core-ktx (si pudieras editar tu respuesta y agregar android-ktx, la votaré)
Yazazzello
@Yazazzello oye, ni siquiera sabía sobre Android KTX, ¡buen punto!
0101100101
7
Para eliminar todos los elementos del SparseArrayuso de los cables de bucle anteriores a Exception.
Para evitar esto Siga el siguiente código para eliminar todos los elementos del SparseArrayuso de bucles 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;}}
El i = -1; Al final no hace nada. También hay un método llamado .clear()que debe ser favorecido.
Paul Woitaschek
¿Por qué usarías un bucle for () en lugar de un while ()? Lo que estás haciendo no tiene sentido para bucle
Phil A
Supongo que Sackurise quería escribir i-=1;para tener en cuenta el elemento que ahora falta. Pero es mejor revertir el ciclo for(int i=sparseArray.size()-1; i>=0; i++){...:; owhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
THS
Referencias como "el bucle anterior" no tienen ningún sentido.
El increíble Jan
Pensé que el punto de un 'iterador' era la eliminación segura de objetos. No he visto ningún ejemplo de la clase Iterator con sparseArrays como hay para hashmaps. Esto se acerca más a abordar la eliminación segura de objetos, espero que funcione sin excepciones de modificaciones concurrentes.
Androidcoder
5
Aquí es simple Iterator<T>e Iterable<T>implementaciones para SparseArray<T>:
Si usa Kotlin, puede usar funciones de extensión como tales, por ejemplo:
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++))}}
También puede convertir a una lista, si lo desea. Ejemplo:
sparseArray.keysIterator().asSequence().toList()
Creo que incluso podría ser seguro eliminar elementos utilizando removeen LongSparseArraysí mismo (no en el iterador), ya que está en orden ascendente.
EDITAR: Parece que hay una manera aún más fácil, usando collection-ktx (ejemplo aquí ). Se implementa de una manera muy similar a lo que escribí, actally.
val sparse=LongSparseArray<String>()for(key in sparse.keyIterator()){}for(value in sparse.valueIterator()){}
sparse.forEach { key, value ->}
Y para aquellos que usan Java, puede usar LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratory LongSparseArrayKt.forEach, por ejemplo. Lo mismo para los otros casos.
La respuesta aceptada tiene algunos agujeros. La belleza de SparseArray es que permite huecos en las indeces. Entonces, podríamos tener dos mapas así, en un SparseArray ...
(0,true)(250,true)
Observe que el tamaño aquí sería 2. Si iteramos sobre el tamaño, solo obtendremos valores para los valores asignados al índice 0 y al índice 1. Por lo tanto, no se accede a la asignación con una clave de 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);}
La mejor manera de hacer esto es iterar sobre el tamaño de su conjunto de datos, luego verificar esas indeces con un get () en la matriz. Aquí hay un ejemplo con un adaptador donde estoy permitiendo la eliminación por lotes de elementos.
for(int index =0; index < mAdapter.getItemCount(); index++){if(toDelete.get(index)==true){long idOfItemToDelete =(allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);}}
Creo que idealmente la familia SparseArray tendría un método getKeys (), pero por desgracia no lo tiene.
Estás equivocado: el keyAtmétodo devuelve el valor de la enésima clave (en tu ejemplo keyAt(1)devolvería 250), que no debe confundirse con el getque devuelve el valor del elemento al que hace referencia la clave.
Eborbob
No estoy seguro de cuál es el "esto" en su comentario. ¿Estás admitiendo que tu respuesta es incorrecta o estás diciendo que mi comentario es incorrecto? Si es esto último, consulte developer.android.com/reference/android/util/…
Eborbob
17
Mi respuesta es incorrecta, no la eliminaré para que otros puedan aprender.
TreeMap<Integer, MyType>
que le permita iterar en orden por clave. Como se indicó, SparseArray está diseñado para ser más eficiente que un HashMap, pero no permite la iteración.Respuestas:
Parece que encontré la solución. No había notado correctamente la
keyAt(index)
función.Entonces iré con algo como esto:
fuente
Object obj = sparseArray.valueAt(i);
valueAt(i)
es más rápido queget(key)
, porquevalueAt(i)
ykeyAt(i)
son O (1) , peroget(key)
es O (log2 n) , por lo que ciertamente siempre lo usaríavalueAt
.Si no le importan las claves,
valueAt(int)
puede utilizarlas mientras realiza una iteración a través de la matriz dispersa para acceder a los valores directamente.fuente
sparseArray.size()
una variable para que no llamesize()
siempre.O bien, simplemente cree su propio ListIterator:
fuente
Simple como pastel. Solo asegúrese de obtener el tamaño de la matriz antes de realizar el bucle.
Espero que esto ayude.
fuente
Para quien esté usando Kotlin, honestamente, la forma más fácil de iterar sobre un SparseArray es: ¡Use la extensión Kotlin de Anko o Android KTX ! (crédito a Yazazzello por señalar Android KTX)
Simplemente llame
forEach { i, item -> }
fuente
Para eliminar todos los elementos del
SparseArray
uso de los cables de bucle anteriores aException
.Para evitar esto Siga el siguiente código para eliminar todos los elementos del
SparseArray
uso de bucles normalesfuente
.clear()
que debe ser favorecido.i-=1;
para tener en cuenta el elemento que ahora falta. Pero es mejor revertir el ciclofor(int i=sparseArray.size()-1; i>=0; i++){...
:; owhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
Aquí es simple
Iterator<T>
eIterable<T>
implementaciones paraSparseArray<T>
:Si desea iterar no solo un valor sino también una clave:
Es útil crear métodos de utilidad que devuelvan
Iterable<T>
yIterable<SparseKeyValue<T>>
:Ahora puedes iterar
SparseArray<T>
:fuente
Si usa Kotlin, puede usar funciones de extensión como tales, por ejemplo:
También puede convertir a una lista, si lo desea. Ejemplo:
Creo que incluso podría ser seguro eliminar elementos utilizando
remove
enLongSparseArray
sí mismo (no en el iterador), ya que está en orden ascendente.EDITAR: Parece que hay una manera aún más fácil, usando collection-ktx (ejemplo aquí ). Se implementa de una manera muy similar a lo que escribí, actally.
Gradle requiere esto:
Aquí está el uso de LongSparseArray:
Y para aquellos que usan Java, puede usar
LongSparseArrayKt.keyIterator
,LongSparseArrayKt.valueIterator
yLongSparseArrayKt.forEach
, por ejemplo. Lo mismo para los otros casos.fuente
La respuesta es no porque
SparseArray
no lo proporciona. En otraspst
palabras, esto no proporciona ninguna interfaz.Puede recorrer
0 - size()
y omitir valores que regresannull
, pero eso es todo.Como digo en mi comentario, si necesita iterar, use a en
Map
lugar de aSparseArray
. Por ejemplo, use unTreeMap
que itera en orden por la tecla.fuente
La respuesta aceptada tiene algunos agujeros. La belleza de SparseArray es que permite huecos en las indeces. Entonces, podríamos tener dos mapas así, en un SparseArray ...
Observe que el tamaño aquí sería 2. Si iteramos sobre el tamaño, solo obtendremos valores para los valores asignados al índice 0 y al índice 1. Por lo tanto, no se accede a la asignación con una clave de 250.
La mejor manera de hacer esto es iterar sobre el tamaño de su conjunto de datos, luego verificar esas indeces con un get () en la matriz. Aquí hay un ejemplo con un adaptador donde estoy permitiendo la eliminación por lotes de elementos.
Creo que idealmente la familia SparseArray tendría un método getKeys (), pero por desgracia no lo tiene.
fuente
keyAt
método devuelve el valor de la enésima clave (en tu ejemplokeyAt(1)
devolvería250
), que no debe confundirse con elget
que devuelve el valor del elemento al que hace referencia la clave.