Możesz użyć narzędzia, TreeMap<Integer, MyType>które pozwala na iterację w kolejności według klucza. Jak wspomniano, SparseArray został zaprojektowany tak, aby był bardziej wydajny niż HashMap, ale nie pozwala na iterację.
John B,
2
bardzo, bardzo mało prawdopodobne jest, że wydajność wybranej mapy będzie wąskim gardłem w Twojej aplikacji.
Jeffrey Blattman
3
@JeffreyBlattman nie oznacza, że powinniśmy unikać używania właściwej struktury, gdy jest to wyraźnie właściwe.
frostymarvellous
1
@frostymarvellous mówi, że jest dwa razy szybszy, co prawdopodobnie oznacza oszczędność poniżej 10 ms. Czy 10ms ma znaczenie w większym programie? Czy warto używać nieoptymalnego interfejsu, który jest trudniejszy do zrozumienia i utrzymania? Nie znam odpowiedzi na te pytania, ale odpowiedź nie brzmi „absolutnie używaj rzadkiej tablicy niezależnie”.
Jeffrey Blattman
Odpowiedzi:
536
Wygląda na to, że znalazłem rozwiązanie. Nie zauważyłem poprawnie tej keyAt(index)funkcji.
Więc pójdę z czymś takim:
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
dokumentacja stwierdza, że „keyAt (int index) Biorąc pod uwagę indeks z zakresu 0 ... size () - 1, zwraca klucz z indeksu mapowania klucz-wartość przechowywanego w tym SparseArray”. więc działa dobrze dla mnie nawet w przypadku opisanym przez ciebie.
Ruzanna,
12
lepiej wstępnie obliczyć rozmiar tablicy i użyć stałej wartości w pętli.
Dmitrij Zajcew
25
Czy nie byłoby łatwiej korzystać z funkcji directAtate tutaj?
Milan Krstic,
34
To również działałoby w pętli:Object obj = sparseArray.valueAt(i);
Florian
27
valueAt(i)jest szybszy niż get(key), ponieważ valueAt(i)i keyAt(i)oba są O (1) , ale get(key)jest O (log2 n) , więc na pewno zawsze użyję valueAt.
Mecki
180
Jeśli nie przejmujesz się kluczami, valueAt(int)możesz to wykorzystać podczas iteracji przez rzadką tablicę, aby uzyskać bezpośredni dostęp do wartości.
for(int i =0, nsize = sparseArray.size(); i < nsize; i++){Object obj = sparseArray.valueAt(i);}
Korzystanie z valueAt () jest użyteczne (i szybsze niż zaakceptowane rozwiązanie), jeśli w twojej iteracji nie zależy na kluczach, tj .: wystąpieniach zliczania pętli o określonej wartości.
Sogger
2
Weź sparseArray.size()jedną zmienną, aby nie zadzwoniła za size()każdym razem.
Pratik Butani
4
Nadmiarowe jest kopiowanie size () do zmiennej. Łatwo to sprawdzić, jeśli spojrzysz na kod metody size (). Nie rozumiem, dlaczego nie powiedziałeś, zanim zasugerowałeś takie rzeczy ... Pamiętam czas 20 lat temu, kiedy mieliśmy proste połączone listy, które naprawdę musiały liczyć ich wielkość za każdym razem, gdy o to prosiłeś, ale nie wierzę że takie rzeczy wciąż istnieją ...
Niesamowity
Czy jest to gwarantowane w kolejności kluczowej?
HughHughTeotl
18
Ooor, po prostu tworzysz swój własny 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();}}}
Dla każdego, kto używa Kotlin, najprostszym sposobem na iterację SparseArray jest: Użyj rozszerzenia Kotlin z Anko lub Androida KTX ! (podziękowania dla Yazazzello za wskazanie Androida KTX)
tak, masz rację. mój zły, spojrzałem na metki i pomyślałem, że Kotlina nie powinno tu być. Ale teraz zastanawiając się, czy ta odpowiedź jest dobrym odniesieniem do samej Kotliny. Chociaż zamiast korzystać z Anko zalecałbym użycie android.github.io/android-ktx/core-ktx (jeśli możesz uprzejmie edytować swoją odpowiedź i dodać android-ktx, to ją głosuję)
Yazazzello
@ Yazazzello hej, nawet nie wiedziałem o Android KTX, dobra uwaga!
0101100101
7
Do usunięcia wszystkich elementów z SparseArrayużycia powyższych pętli prowadzi do Exception.
Aby tego uniknąć Postępuj zgodnie z poniższym kodem, aby usunąć wszystkie elementy z SparseArraynormalnych pętli
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;}}
I = -1; na końcu nic nie robi. Istnieje również metoda o nazwie, .clear()która powinna być preferowana.
Paul Woitaschek
Dlaczego miałbyś używać pętli for () zamiast while ()? To, co robisz, nie ma sensu zapętlać
Phil A
Zakładam, że Sackurise chciał napisać, i-=1;aby uwzględnić brakujący element. Ale to lepiej, aby przywrócić pętlę: for(int i=sparseArray.size()-1; i>=0; i++){...; lubwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
ths
Odnośniki takie jak „powyższa pętla” nie mają żadnego sensu.
Niesamowity
Myślałem, że celem „iteratora” jest bezpieczne usuwanie obiektów. Nie widziałem żadnych przykładów klasy Iterator z rzadkimi tablicami, tak jak w przypadku map skrótów. Jest to najbliższe rozwiązaniu problemu bezpiecznego usuwania obiektów, mam nadzieję, że zadziała bez jednoczesnych wyjątków modyfikacji.
Androidcoder
5
Oto proste Iterator<T>i Iterable<T>implementacje dla SparseArray<T>:
Jeśli korzystasz z Kotlin, możesz używać funkcji rozszerzeń jako takich, na przykład:
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++))}}
Możesz także przekonwertować na listę, jeśli chcesz. Przykład:
sparseArray.keysIterator().asSequence().toList()
Myślę, że może to być nawet bezpieczne usuwanie elementów za pomocą removena LongSparseArraysiebie (nie na iterator), jak to jest w porządku rosnącym.
EDYCJA: Wydaje się, że istnieje jeszcze łatwiejszy sposób, używając collection-ktx (przykład tutaj ). Zaimplementowano go bardzo podobnie do tego, co napisałem.
val sparse=LongSparseArray<String>()for(key in sparse.keyIterator()){}for(value in sparse.valueIterator()){}
sparse.forEach { key, value ->}
A dla tych, którzy używają Java, możesz użyć LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratori LongSparseArrayKt.forEachna przykład. To samo dotyczy innych przypadków.
Odpowiedź brzmi „nie”, ponieważ SparseArraynie zawiera odpowiedzi. Tak jakpst to ujęto, ta rzecz nie zapewnia żadnych interfejsów.
Możesz zapętlać 0 - size()i pomijać zwracane wartościnull , ale o to chodzi.
Jak stwierdzam w moim komentarzu, jeśli chcesz iterować, użyj Mapzamiast zamiast SparseArray. Na przykład użyj TreeMapiteracji w kolejności według klucza.
W zaakceptowanej odpowiedzi są pewne dziury. Piękno SparseArray polega na tym, że pozwala na luki w indkach. Moglibyśmy mieć dwie takie mapy w SparseArray ...
(0,true)(250,true)
Zauważ, że tutaj rozmiar wynosiłby 2. Jeśli iterujemy ponad rozmiarem, otrzymamy tylko wartości dla wartości mapowanych na indeks 0 i indeks 1. Zatem mapowanie za pomocą klucza 250 nie jest dostępne.
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
Najlepszym sposobem na to jest iteracja wielkości zbioru danych, a następnie sprawdzenie tych indek za pomocą get () w tablicy. Oto przykład z adapterem, w którym zezwalam na grupowe usuwanie elementów.
for(int index =0; index < mAdapter.getItemCount(); index++){if(toDelete.get(index)==true){long idOfItemToDelete =(allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);}}
Myślę, że idealnie rodzina SparseArray miałaby metodę getKeys (), ale niestety nie.
Mylisz się - keyAtmetoda zwraca wartość n-tego klucza (w twoim przykładzie keyAt(1)powróciłoby 250), nie mylić z getktórą zwraca wartość elementu, do którego odwołuje się klucz.
Eborbob,
Nie jestem pewien, co to jest w twoim komentarzu. Czy przyznajesz, że twoja odpowiedź jest błędna, czy twierdzisz, że mój komentarz jest błędny? Jeśli to ostatnie, sprawdź developer.android.com/reference/android/util/…
Eborbob
17
Moja odpowiedź jest błędna, nie usunę jej, aby inni mogli się uczyć.
TreeMap<Integer, MyType>
które pozwala na iterację w kolejności według klucza. Jak wspomniano, SparseArray został zaprojektowany tak, aby był bardziej wydajny niż HashMap, ale nie pozwala na iterację.Odpowiedzi:
Wygląda na to, że znalazłem rozwiązanie. Nie zauważyłem poprawnie tej
keyAt(index)
funkcji.Więc pójdę z czymś takim:
źródło
Object obj = sparseArray.valueAt(i);
valueAt(i)
jest szybszy niżget(key)
, ponieważvalueAt(i)
ikeyAt(i)
oba są O (1) , aleget(key)
jest O (log2 n) , więc na pewno zawsze użyjęvalueAt
.Jeśli nie przejmujesz się kluczami,
valueAt(int)
możesz to wykorzystać podczas iteracji przez rzadką tablicę, aby uzyskać bezpośredni dostęp do wartości.źródło
sparseArray.size()
jedną zmienną, aby nie zadzwoniła zasize()
każdym razem.Ooor, po prostu tworzysz swój własny ListIterator:
źródło
Proste jak ciasto. Upewnij się, że pobierasz rozmiar tablicy przed wykonaniem pętli.
Mam nadzieję że to pomoże.
źródło
Dla każdego, kto używa Kotlin, najprostszym sposobem na iterację SparseArray jest: Użyj rozszerzenia Kotlin z Anko lub Androida KTX ! (podziękowania dla Yazazzello za wskazanie Androida KTX)
Po prostu zadzwoń
forEach { i, item -> }
źródło
Do usunięcia wszystkich elementów z
SparseArray
użycia powyższych pętli prowadzi doException
.Aby tego uniknąć Postępuj zgodnie z poniższym kodem, aby usunąć wszystkie elementy z
SparseArray
normalnych pętliźródło
.clear()
która powinna być preferowana.i-=1;
aby uwzględnić brakujący element. Ale to lepiej, aby przywrócić pętlę:for(int i=sparseArray.size()-1; i>=0; i++){...
; lubwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
Oto proste
Iterator<T>
iIterable<T>
implementacje dlaSparseArray<T>
:Jeśli chcesz iterować nie tylko wartość, ale także klucz:
Przydatne jest tworzenie zwracających się metod narzędziowych
Iterable<T>
iIterable<SparseKeyValue<T>>
:Teraz możesz iterować
SparseArray<T>
:źródło
Jeśli korzystasz z Kotlin, możesz używać funkcji rozszerzeń jako takich, na przykład:
Możesz także przekonwertować na listę, jeśli chcesz. Przykład:
Myślę, że może to być nawet bezpieczne usuwanie elementów za pomocą
remove
naLongSparseArray
siebie (nie na iterator), jak to jest w porządku rosnącym.EDYCJA: Wydaje się, że istnieje jeszcze łatwiejszy sposób, używając collection-ktx (przykład tutaj ). Zaimplementowano go bardzo podobnie do tego, co napisałem.
Gradle wymaga tego:
Oto zastosowanie LongSparseArray:
A dla tych, którzy używają Java, możesz użyć
LongSparseArrayKt.keyIterator
,LongSparseArrayKt.valueIterator
iLongSparseArrayKt.forEach
na przykład. To samo dotyczy innych przypadków.źródło
Odpowiedź brzmi „nie”, ponieważ
SparseArray
nie zawiera odpowiedzi. Tak jakpst
to ujęto, ta rzecz nie zapewnia żadnych interfejsów.Możesz zapętlać
0 - size()
i pomijać zwracane wartościnull
, ale o to chodzi.Jak stwierdzam w moim komentarzu, jeśli chcesz iterować, użyj
Map
zamiast zamiastSparseArray
. Na przykład użyjTreeMap
iteracji w kolejności według klucza.źródło
W zaakceptowanej odpowiedzi są pewne dziury. Piękno SparseArray polega na tym, że pozwala na luki w indkach. Moglibyśmy mieć dwie takie mapy w SparseArray ...
Zauważ, że tutaj rozmiar wynosiłby 2. Jeśli iterujemy ponad rozmiarem, otrzymamy tylko wartości dla wartości mapowanych na indeks 0 i indeks 1. Zatem mapowanie za pomocą klucza 250 nie jest dostępne.
Najlepszym sposobem na to jest iteracja wielkości zbioru danych, a następnie sprawdzenie tych indek za pomocą get () w tablicy. Oto przykład z adapterem, w którym zezwalam na grupowe usuwanie elementów.
Myślę, że idealnie rodzina SparseArray miałaby metodę getKeys (), ale niestety nie.
źródło
keyAt
metoda zwraca wartość n-tego klucza (w twoim przykładziekeyAt(1)
powróciłoby250
), nie mylić zget
którą zwraca wartość elementu, do którego odwołuje się klucz.