Apakah ada cara untuk beralih ke Java SparseArray (untuk Android)? Saya terbiasa sparsearraydengan mudah mendapatkan nilai berdasarkan indeks. Saya tidak dapat menemukannya.
Anda bisa menggunakan TreeMap<Integer, MyType>yang akan memungkinkan Anda untuk mengulang secara berurutan. Seperti yang dinyatakan, SparseArray dirancang agar lebih efisien daripada HashMap, tetapi tidak memungkinkan iterasi.
John B
2
itu sangat, sangat tidak mungkin bahwa kinerja imp peta yang Anda pilih akan menjadi hambatan dalam aplikasi Anda.
Jeffrey Blattman
3
@JeffreyBlattman tidak berarti kita harus menghindari menggunakan struktur yang tepat ketika itu jelas tepat.
frostymarvelous
1
@ Frostymarvelous mengatakan ini DUA KALI lebih cepat, itu mungkin berarti penghematan kurang dari 10 ms. Apakah 10 ms relevan dalam skema aplikasi yang lebih besar? Apakah layak menggunakan antarmuka sub-optimal yang sulit dipahami dan dipelihara? Saya tidak tahu jawaban untuk hal-hal itu, tetapi jawabannya bukan "benar-benar menggunakan array jarang".
Jeffrey Blattman
Jawaban:
537
Sepertinya saya menemukan solusinya. Saya belum benar-benar memperhatikan keyAt(index)fungsinya.
Jadi saya akan pergi dengan sesuatu seperti ini:
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
dokumentasi menyatakan bahwa "keyAt (int index) Diberikan indeks dalam kisaran 0 ... size () - 1, mengembalikan kunci dari pemetaan nilai kunci indeks yang disimpan SparseArray ini." jadi itu berfungsi dengan baik untuk saya bahkan untuk kasus yang dijelaskan oleh Anda.
Ruzanna
12
lebih baik untuk menghitung ulang ukuran array dan menggunakan nilai konstan dalam loop.
Dmitry Zaytsev
25
Bukankah lebih mudah menggunakan fungsi valueAt secara langsung di sini?
Milan Krstic
34
Ini akan bekerja juga di dalam loop:Object obj = sparseArray.valueAt(i);
Florian
27
valueAt(i)lebih cepat dari get(key), karena valueAt(i)dan keyAt(i)keduanya O (1) , tetapi get(key)adalah O (log2 n) , jadi saya pasti selalu menggunakan valueAt.
Mecki
180
Jika Anda tidak peduli dengan kunci, maka valueAt(int)dapat digunakan untuk sementara iterasi melalui array jarang untuk mengakses nilai secara langsung.
for(int i =0, nsize = sparseArray.size(); i < nsize; i++){Object obj = sparseArray.valueAt(i);}
Menggunakan valueAt () berguna (dan lebih cepat dari solusi yang diterima) jika iterasi Anda tidak peduli dengan kunci, yaitu: kejadian penghitungan loop dari nilai tertentu.
Sogger
2
Ambil sparseArray.size()satu variabel sehingga tidak akan menelepon size()setiap waktu.
Pratik Butani
4
Itu berlebihan untuk menyalin ukuran () ke variabel. Mudah untuk memeriksa apakah Anda hanya melihat kode metode size (). Saya tidak dapat mengerti mengapa Anda tidak sebelum menyarankan hal-hal seperti itu ... Saya ingat suatu waktu 20 tahun yang lalu di mana kami memiliki daftar tertaut sederhana yang benar-benar harus menghitung ukurannya setiap kali Anda memintanya, tetapi saya tidak percaya bahwa hal-hal seperti itu masih ada ...
Jan
Apakah ini dijamin dalam urutan utama?
HughHughTeotl
18
Atau Anda baru saja membuat ListIterator Anda sendiri:
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();}}}
Bagi siapa pun yang menggunakan Kotlin, jujur cara termudah untuk beralih di SparseArray adalah: Gunakan ekstensi Kotlin dari Anko atau Android KTX ! (kredit untuk Yazazzello karena menunjukkan Android KTX)
ya, Anda sebenarnya benar. salahku, aku melihat pada tag dan berpikir bahwa Kotlin seharusnya tidak berada di sini. Tetapi sekarang memiliki pemikiran kedua bahwa jawaban ini adalah referensi yang baik untuk Kotlin itu sendiri. Meskipun alih-alih menggunakan Anko, saya sarankan untuk menggunakan android.github.io/android-ktx/core-ktx (jika Anda bisa mengedit jawaban dengan baik dan menambahkan android-ktx saya akan
membatalkannya
@Yazazzello, hei, saya bahkan tidak tahu tentang Android KTX, poin bagus!
0101100101
7
Untuk menghapus semua elemen dari SparseArraymenggunakan mengarah perulangan di atas Exception.
Untuk menghindari ini Ikuti kode di bawah ini untuk menghapus semua elemen dari SparseArraymenggunakan loop normal
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; pada akhirnya tidak melakukan apa-apa. Juga ada metode yang disebut .clear()yang harus disukai.
Paul Woitaschek
Mengapa Anda menggunakan loop for () alih-alih sementara ()? Apa yang Anda lakukan tidak masuk akal untuk diulang
Phil A
Saya berasumsi Sackurise ingin menulis i-=1;ke akun untuk elemen yang sekarang hilang. Tapi itu lebih baik untuk kembali loop: for(int i=sparseArray.size()-1; i>=0; i++){...; atauwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
THS
Referensi seperti "perulangan di atas" tidak masuk akal sama sekali.
Luar biasa
Saya pikir titik 'iterator' adalah penghapusan objek yang aman. Saya belum melihat contoh kelas Iterator dengan sparseArrays seperti ada untuk hashmaps. Ini yang paling dekat dengan mengatasi penghapusan objek aman, saya harap ini berfungsi tanpa pengecualian modifikasi bersamaan.
Androidcoder
5
Berikut ini sederhana Iterator<T>dan Iterable<T>implementasi untuk SparseArray<T>:
Jika Anda menggunakan Kotlin, Anda dapat menggunakan fungsi ekstensi seperti itu, misalnya:
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++))}}
Anda juga dapat mengonversi ke daftar, jika diinginkan. Contoh:
sparseArray.keysIterator().asSequence().toList()
Saya pikir itu bahkan mungkin aman untuk item menghapus menggunakan removepada LongSparseArrayitu sendiri (bukan pada iterator), seperti di urutan menaik.
EDIT: Sepertinya ada cara yang lebih mudah, dengan menggunakan collection-ktx (contoh di sini ). Ini diterapkan dengan cara yang sangat mirip dengan apa yang saya tulis, secara aktif.
val sparse=LongSparseArray<String>()for(key in sparse.keyIterator()){}for(value in sparse.valueIterator()){}
sparse.forEach { key, value ->}
Dan bagi mereka yang menggunakan Java, Anda dapat menggunakan LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratordan LongSparseArrayKt.forEach, misalnya. Sama untuk kasus lainnya.
Jawabannya adalah tidak karena SparseArraytidak menyediakannya. Seperti yang pstdikatakan, benda ini tidak menyediakan antarmuka apa pun.
Anda bisa mengulang dari 0 - size()dan melewatkan nilai yang kembali null, tetapi hanya itu saja.
Seperti yang saya nyatakan dalam komentar saya, jika Anda perlu menggunakan it Mapbukan a SparseArray. Misalnya, gunakan TreeMaptombol yang diurutkan berdasarkan kunci.
Jawaban yang diterima memiliki beberapa lubang di dalamnya. Keindahan SparseArray adalah memungkinkan celah di indeces. Jadi, kita bisa memiliki dua peta seperti itu, dalam SparseArray ...
(0,true)(250,true)
Perhatikan bahwa ukuran di sini adalah 2. Jika kita mengulangi ukuran lebih, kita hanya akan mendapatkan nilai untuk nilai yang dipetakan ke indeks 0 dan indeks 1. Jadi pemetaan dengan kunci 250 tidak diakses.
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
Cara terbaik untuk melakukan ini adalah untuk mengulangi ukuran set data Anda, kemudian periksa indeces tersebut dengan get () pada array. Berikut ini adalah contoh dengan adaptor di mana saya mengizinkan penghapusan beberapa item.
for(int index =0; index < mAdapter.getItemCount(); index++){if(toDelete.get(index)==true){long idOfItemToDelete =(allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);}}
Saya pikir idealnya keluarga SparseArray akan memiliki metode getKeys (), tetapi sayangnya tidak.
Anda salah - keyAtmetode mengembalikan nilai kunci ke-n (dalam contoh Anda keyAt(1)akan kembali 250), jangan bingung dengan getyang mengembalikan nilai elemen yang dirujuk oleh kunci.
Eborbob
Saya tidak yakin apa 'ini' dalam komentar Anda. Apakah Anda mengakui bahwa jawaban Anda salah, atau apakah Anda mengatakan bahwa komentar saya salah? Jika yang terakhir silakan periksa developer.android.com/reference/android/util/…
Eborbob
17
Jawaban saya salah, saya tidak akan menghapusnya sehingga orang lain bisa belajar.
TreeMap<Integer, MyType>
yang akan memungkinkan Anda untuk mengulang secara berurutan. Seperti yang dinyatakan, SparseArray dirancang agar lebih efisien daripada HashMap, tetapi tidak memungkinkan iterasi.Jawaban:
Sepertinya saya menemukan solusinya. Saya belum benar-benar memperhatikan
keyAt(index)
fungsinya.Jadi saya akan pergi dengan sesuatu seperti ini:
sumber
Object obj = sparseArray.valueAt(i);
valueAt(i)
lebih cepat dariget(key)
, karenavalueAt(i)
dankeyAt(i)
keduanya O (1) , tetapiget(key)
adalah O (log2 n) , jadi saya pasti selalu menggunakanvalueAt
.Jika Anda tidak peduli dengan kunci, maka
valueAt(int)
dapat digunakan untuk sementara iterasi melalui array jarang untuk mengakses nilai secara langsung.sumber
sparseArray.size()
satu variabel sehingga tidak akan meneleponsize()
setiap waktu.Atau Anda baru saja membuat ListIterator Anda sendiri:
sumber
Sederhana seperti Pie. Pastikan Anda mengambil ukuran array sebelum benar-benar melakukan loop.
Semoga ini membantu.
sumber
Bagi siapa pun yang menggunakan Kotlin, jujur cara termudah untuk beralih di SparseArray adalah: Gunakan ekstensi Kotlin dari Anko atau Android KTX ! (kredit untuk Yazazzello karena menunjukkan Android KTX)
Cukup telepon
forEach { i, item -> }
sumber
Untuk menghapus semua elemen dari
SparseArray
menggunakan mengarah perulangan di atasException
.Untuk menghindari ini Ikuti kode di bawah ini untuk menghapus semua elemen dari
SparseArray
menggunakan loop normalsumber
.clear()
yang harus disukai.i-=1;
ke akun untuk elemen yang sekarang hilang. Tapi itu lebih baik untuk kembali loop:for(int i=sparseArray.size()-1; i>=0; i++){...
; atauwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
Berikut ini sederhana
Iterator<T>
danIterable<T>
implementasi untukSparseArray<T>
:Jika Anda ingin mengulangi tidak hanya nilai tetapi juga kunci:
Berguna untuk membuat metode utilitas yang mengembalikan
Iterable<T>
danIterable<SparseKeyValue<T>>
:Sekarang Anda dapat mengulangi
SparseArray<T>
:sumber
Jika Anda menggunakan Kotlin, Anda dapat menggunakan fungsi ekstensi seperti itu, misalnya:
Anda juga dapat mengonversi ke daftar, jika diinginkan. Contoh:
Saya pikir itu bahkan mungkin aman untuk item menghapus menggunakan
remove
padaLongSparseArray
itu sendiri (bukan pada iterator), seperti di urutan menaik.EDIT: Sepertinya ada cara yang lebih mudah, dengan menggunakan collection-ktx (contoh di sini ). Ini diterapkan dengan cara yang sangat mirip dengan apa yang saya tulis, secara aktif.
Gradle membutuhkan ini:
Inilah penggunaan untuk LongSparseArray:
Dan bagi mereka yang menggunakan Java, Anda dapat menggunakan
LongSparseArrayKt.keyIterator
,LongSparseArrayKt.valueIterator
danLongSparseArrayKt.forEach
, misalnya. Sama untuk kasus lainnya.sumber
Jawabannya adalah tidak karena
SparseArray
tidak menyediakannya. Seperti yangpst
dikatakan, benda ini tidak menyediakan antarmuka apa pun.Anda bisa mengulang dari
0 - size()
dan melewatkan nilai yang kembalinull
, tetapi hanya itu saja.Seperti yang saya nyatakan dalam komentar saya, jika Anda perlu menggunakan it
Map
bukan aSparseArray
. Misalnya, gunakanTreeMap
tombol yang diurutkan berdasarkan kunci.sumber
Jawaban yang diterima memiliki beberapa lubang di dalamnya. Keindahan SparseArray adalah memungkinkan celah di indeces. Jadi, kita bisa memiliki dua peta seperti itu, dalam SparseArray ...
Perhatikan bahwa ukuran di sini adalah 2. Jika kita mengulangi ukuran lebih, kita hanya akan mendapatkan nilai untuk nilai yang dipetakan ke indeks 0 dan indeks 1. Jadi pemetaan dengan kunci 250 tidak diakses.
Cara terbaik untuk melakukan ini adalah untuk mengulangi ukuran set data Anda, kemudian periksa indeces tersebut dengan get () pada array. Berikut ini adalah contoh dengan adaptor di mana saya mengizinkan penghapusan beberapa item.
Saya pikir idealnya keluarga SparseArray akan memiliki metode getKeys (), tetapi sayangnya tidak.
sumber
keyAt
metode mengembalikan nilai kunci ke-n (dalam contoh AndakeyAt(1)
akan kembali250
), jangan bingung denganget
yang mengembalikan nilai elemen yang dirujuk oleh kunci.