Gibt es eine Möglichkeit, über Java SparseArray (für Android) zu iterieren? Früher habe ich sparsearrayleicht Werte nach Index erhalten. Ich konnte keinen finden.
Wow, sprechen Sie über eine völlig ungeliebte Klasse , entspricht ZERO-Sammlungsschnittstellen ...
1
Sie können ein verwenden, mit TreeMap<Integer, MyType>dem Sie nach Schlüssel sortieren können. Wie bereits erwähnt, ist SparseArray effizienter als eine HashMap, erlaubt jedoch keine Iteration.
John B
2
Es ist sehr, sehr unwahrscheinlich, dass die Leistung des von Ihnen ausgewählten Kartenimplikats der Engpass in Ihrer App ist.
Jeffrey Blattman
3
@ JeffreyBlattman bedeutet nicht, dass wir die Verwendung der richtigen Struktur vermeiden sollten, wenn dies eindeutig angemessen ist.
frostymarvelous
1
@frostymarvelous sagen, es ist ZWEIMAL so schnell, das bedeutet wahrscheinlich eine Einsparung von weniger als 10 ms. Sind 10 ms im größeren Schema der App relevant? Lohnt es sich, eine suboptimale Schnittstelle zu verwenden, die schwerer zu verstehen und zu warten ist? Ich kenne die Antwort auf diese Dinge nicht, aber die Antwort lautet nicht "absolut spärliches Array verwenden, unabhängig davon".
Jeffrey Blattman
Antworten:
537
Scheint, als hätte ich die Lösung gefunden. Ich hatte die keyAt(index)Funktion nicht richtig bemerkt .
Also gehe ich mit so etwas:
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
In der Dokumentation heißt es: "keyAt (int index) Gibt bei einem Index im Bereich von 0 ... size () - 1 den Schlüssel aus der indexierten Schlüsselwertzuordnung zurück, die dieses SparseArray speichert." So funktioniert es auch für den von Ihnen beschriebenen Fall.
Ruzanna
12
Es ist besser, die Größe des Arrays vorab zu berechnen und einen konstanten Wert in der Schleife zu verwenden.
Dmitry Zaytsev
25
Wäre es nicht einfacher, hier direkt die Funktion valueAt zu verwenden?
Milan Krstic
34
Dies würde auch innerhalb der Schleife funktionieren:Object obj = sparseArray.valueAt(i);
Florian
27
valueAt(i)ist schneller als get(key), weil valueAt(i)und keyAt(i)beide O (1) sind , aber get(key)ist O (log2 n) , also würde ich sicher immer verwenden valueAt.
Mecki
180
Wenn Sie sich nicht für die Schlüssel interessieren, valueAt(int)können Sie beim Durchlaufen des spärlichen Arrays direkt auf die Werte zugreifen.
for(int i =0, nsize = sparseArray.size(); i < nsize; i++){Object obj = sparseArray.valueAt(i);}
Die Verwendung von valueAt () ist nützlich (und schneller als die akzeptierte Lösung), wenn Ihre Iteration sich nicht um die Schlüssel kümmert, dh: eine Schleife, die Vorkommen eines bestimmten Werts zählt.
Sogger
2
Nehmen Sie sparseArray.size()eine Variable auf, damit sie nicht size()jedes Mal aufgerufen wird.
Pratik Butani
4
Es ist überflüssig, size () in eine Variable zu kopieren. Einfach zu überprüfen, ob Sie sich nur den Code der size () -Methode ansehen. Ich kann nicht verstehen, warum Sie es nicht getan haben, bevor Sie solche Dinge vorgeschlagen haben ... Ich erinnere mich an eine Zeit vor 20 Jahren, als wir einfache verknüpfte Listen hatten, die wirklich jedes Mal ihre Größe zählen mussten, wenn Sie sie danach fragten, aber ich glaube nicht dass solche Dinge noch existieren ...
Der unglaubliche
Ist dies garantiert in Schlüsselreihenfolge?
HughHughTeotl
18
Oder Sie erstellen einfach Ihren eigenen 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();}}}
Für jeden, der Kotlin verwendet, ist der mit Abstand einfachste Weg, über ein SparseArray zu iterieren, ehrlich: Verwenden Sie die Kotlin-Erweiterung von Anko oder Android KTX ! (Dank an Yazazzello für den Hinweis auf Android KTX)
Ja, du hast tatsächlich recht. Mein schlechtes, ich schaute auf die Tags und dachte, dass Kotlin nicht hier sein sollte. Aber jetzt ein zweiter Gedanke, dass diese Antwort ein guter Hinweis auf Kotlin selbst ist. Obwohl ich anstelle von Anko empfehlen würde, android.github.io/android-ktx/core-ktx zu verwenden (wenn Sie Ihre Antwort freundlicherweise bearbeiten und android-ktx hinzufügen könnten, werde ich sie verbessern)
Yazazzello
@ Yazazzello hey ich wusste nicht mal über Android KTX, guter Punkt!
0101100101
7
Zum Entfernen aller Elemente aus der SparseArrayVerwendung führt die obige Schleife zu Exception.
Um dies zu vermeiden, befolgen Sie den folgenden Code, um alle Elemente aus der SparseArrayVerwendung normaler Schleifen zu entfernen
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;}}
Das i = -1; am Ende macht nichts. Es gibt auch eine Methode, .clear()die bevorzugt werden sollte.
Paul Woitaschek
Warum sollten Sie eine for () - Schleife anstelle einer while () verwenden? Was Sie tun, macht keinen Sinn für eine Schleife
Phil A
Ich gehe davon aus, dass Sackurise schreiben wollte, um i-=1;das jetzt fehlende Element zu berücksichtigen. Es ist jedoch besser, die Schleife zurückzusetzen : for(int i=sparseArray.size()-1; i>=0; i++){...; oderwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
am
Referenzen wie "die obige Schleife" machen überhaupt keinen Sinn.
Der unglaubliche
Ich dachte, der Punkt eines "Iterators" sei die sichere Entfernung von Objekten. Ich habe keine Beispiele für die Iterator-Klasse mit sparseArrays gesehen, wie es sie für Hashmaps gibt. Dies kommt der sicheren Entfernung von Objekten am nächsten. Ich hoffe, es funktioniert ohne gleichzeitige Änderungsausnahmen.
Androidcoder
5
Hier ist einfach Iterator<T>und Iterable<T>Implementierungen für SparseArray<T>:
Wenn Sie Kotlin verwenden, können Sie Erweiterungsfunktionen als solche verwenden, zum Beispiel:
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++))}}
Wenn Sie möchten, können Sie auch in eine Liste konvertieren. Beispiel:
sparseArray.keysIterator().asSequence().toList()
Ich denke , es könnte sogar zu löschen Gegenstände sicher sein , mit removeauf der LongSparseArrayselbst (nicht auf dem Iterator), wie sie in aufsteigender Reihenfolge ist.
BEARBEITEN: Es scheint noch einfacher zu sein, mit collection-ktx (Beispiel hier) ). Es ist sehr ähnlich implementiert wie das, was ich tatsächlich geschrieben habe.
val sparse=LongSparseArray<String>()for(key in sparse.keyIterator()){}for(value in sparse.valueIterator()){}
sparse.forEach { key, value ->}
Und für diejenigen , die Java verwenden, können Sie verwenden LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratorund LongSparseArrayKt.forEachzum Beispiel. Gleiches gilt für die anderen Fälle.
Die Antwort ist nein, weil SparseArrayes nicht liefert. Wie pstgesagt, dieses Ding bietet keine Schnittstellen.
Sie könnten 0 - size()Werte, die zurückgeben null, durchlaufen und überspringen , aber das ist es auch.
Wie ich in meinem Kommentar feststelle, verwenden Sie a Mapanstelle von a , wenn Sie iterieren müssen SparseArray. Verwenden Sie beispielsweise a, TreeMapdas in der Reihenfolge des Schlüssels iteriert.
Die akzeptierte Antwort enthält einige Lücken. Das Schöne am SparseArray ist, dass es Lücken in den Unanständigkeiten zulässt. Wir könnten also zwei Karten wie diese in einem SparseArray haben ...
(0,true)(250,true)
Beachten Sie, dass die Größe hier 2 wäre. Wenn wir über die Größe iterieren, erhalten wir nur Werte für die Werte, die Index 0 und Index 1 zugeordnet sind. Auf die Zuordnung mit einem Schlüssel von 250 wird also nicht zugegriffen.
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
Der beste Weg, dies zu tun, besteht darin, über die Größe Ihres Datensatzes zu iterieren und diese Unabhängigkeiten dann mit einem get () im Array zu überprüfen. Hier ist ein Beispiel mit einem Adapter, bei dem ich das Batch-Löschen von Elementen erlaube.
for(int index =0; index < mAdapter.getItemCount(); index++){if(toDelete.get(index)==true){long idOfItemToDelete =(allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);}}
Ich denke, im Idealfall hätte die SparseArray-Familie eine getKeys () -Methode, aber leider nicht.
Sie liegen falsch - die keyAtMethode gibt den Wert des n-ten Schlüssels zurück (in Ihrem Beispiel keyAt(1)würde zurückgegeben 250), nicht zu verwechseln mit getdem Wert des Elements, auf das der Schlüssel verweist.
Eborbob
Ich bin mir nicht sicher, was das "Dies" in Ihrem Kommentar ist. Geben Sie zu, dass Ihre Antwort falsch ist, oder sagen Sie, dass mein Kommentar falsch ist? Wenn letzteres der
Fall ist,
17
Meine Antwort ist falsch, ich werde sie nicht löschen, damit andere lernen können.
TreeMap<Integer, MyType>
dem Sie nach Schlüssel sortieren können. Wie bereits erwähnt, ist SparseArray effizienter als eine HashMap, erlaubt jedoch keine Iteration.Antworten:
Scheint, als hätte ich die Lösung gefunden. Ich hatte die
keyAt(index)
Funktion nicht richtig bemerkt .Also gehe ich mit so etwas:
quelle
Object obj = sparseArray.valueAt(i);
valueAt(i)
ist schneller alsget(key)
, weilvalueAt(i)
undkeyAt(i)
beide O (1) sind , aberget(key)
ist O (log2 n) , also würde ich sicher immer verwendenvalueAt
.Wenn Sie sich nicht für die Schlüssel interessieren,
valueAt(int)
können Sie beim Durchlaufen des spärlichen Arrays direkt auf die Werte zugreifen.quelle
sparseArray.size()
eine Variable auf, damit sie nichtsize()
jedes Mal aufgerufen wird.Oder Sie erstellen einfach Ihren eigenen ListIterator:
quelle
Einfach wie Torte. Stellen Sie einfach sicher, dass Sie die Arraygröße abrufen, bevor Sie die Schleife tatsächlich ausführen.
Hoffe das hilft.
quelle
Für jeden, der Kotlin verwendet, ist der mit Abstand einfachste Weg, über ein SparseArray zu iterieren, ehrlich: Verwenden Sie die Kotlin-Erweiterung von Anko oder Android KTX ! (Dank an Yazazzello für den Hinweis auf Android KTX)
Einfach anrufen
forEach { i, item -> }
quelle
Zum Entfernen aller Elemente aus der
SparseArray
Verwendung führt die obige Schleife zuException
.Um dies zu vermeiden, befolgen Sie den folgenden Code, um alle Elemente aus der
SparseArray
Verwendung normaler Schleifen zu entfernenquelle
.clear()
die bevorzugt werden sollte.i-=1;
das jetzt fehlende Element zu berücksichtigen. Es ist jedoch besser, die Schleife zurückzusetzen :for(int i=sparseArray.size()-1; i>=0; i++){...
; oderwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
Hier ist einfach
Iterator<T>
undIterable<T>
Implementierungen fürSparseArray<T>
:Wenn Sie nicht nur einen Wert, sondern auch einen Schlüssel iterieren möchten:
Es ist nützlich, Dienstprogrammmethoden zu erstellen, die Folgendes zurückgeben
Iterable<T>
undIterable<SparseKeyValue<T>>
:Jetzt können Sie iterieren
SparseArray<T>
:quelle
Wenn Sie Kotlin verwenden, können Sie Erweiterungsfunktionen als solche verwenden, zum Beispiel:
Wenn Sie möchten, können Sie auch in eine Liste konvertieren. Beispiel:
Ich denke , es könnte sogar zu löschen Gegenstände sicher sein , mit
remove
auf derLongSparseArray
selbst (nicht auf dem Iterator), wie sie in aufsteigender Reihenfolge ist.BEARBEITEN: Es scheint noch einfacher zu sein, mit collection-ktx (Beispiel hier) ). Es ist sehr ähnlich implementiert wie das, was ich tatsächlich geschrieben habe.
Gradle benötigt dies:
Hier ist die Verwendung für LongSparseArray:
Und für diejenigen , die Java verwenden, können Sie verwenden
LongSparseArrayKt.keyIterator
,LongSparseArrayKt.valueIterator
undLongSparseArrayKt.forEach
zum Beispiel. Gleiches gilt für die anderen Fälle.quelle
Die Antwort ist nein, weil
SparseArray
es nicht liefert. Wiepst
gesagt, dieses Ding bietet keine Schnittstellen.Sie könnten
0 - size()
Werte, die zurückgebennull
, durchlaufen und überspringen , aber das ist es auch.Wie ich in meinem Kommentar feststelle, verwenden Sie a
Map
anstelle von a , wenn Sie iterieren müssenSparseArray
. Verwenden Sie beispielsweise a,TreeMap
das in der Reihenfolge des Schlüssels iteriert.quelle
Die akzeptierte Antwort enthält einige Lücken. Das Schöne am SparseArray ist, dass es Lücken in den Unanständigkeiten zulässt. Wir könnten also zwei Karten wie diese in einem SparseArray haben ...
Beachten Sie, dass die Größe hier 2 wäre. Wenn wir über die Größe iterieren, erhalten wir nur Werte für die Werte, die Index 0 und Index 1 zugeordnet sind. Auf die Zuordnung mit einem Schlüssel von 250 wird also nicht zugegriffen.
Der beste Weg, dies zu tun, besteht darin, über die Größe Ihres Datensatzes zu iterieren und diese Unabhängigkeiten dann mit einem get () im Array zu überprüfen. Hier ist ein Beispiel mit einem Adapter, bei dem ich das Batch-Löschen von Elementen erlaube.
Ich denke, im Idealfall hätte die SparseArray-Familie eine getKeys () -Methode, aber leider nicht.
quelle
keyAt
Methode gibt den Wert des n-ten Schlüssels zurück (in Ihrem BeispielkeyAt(1)
würde zurückgegeben250
), nicht zu verwechseln mitget
dem Wert des Elements, auf das der Schlüssel verweist.