Вы можете использовать, TreeMap<Integer, MyType>что позволит вам итерировать в порядке по ключу. Как уже говорилось, SparseArray разработан, чтобы быть более эффективным, чем HashMap, но он не допускает итерации.
Джон Б
2
очень, очень маловероятно, что производительность выбранной вами карты станет узким местом в вашем приложении.
Джеффри Блаттман
3
@JeffreyBlattman не означает, что мы должны избегать использования правильной структуры, когда она явно уместна.
морозный
1
@frostymarvelous говорят, что это в два раза быстрее, что, вероятно, означает экономию менее 10 мс. Имеет ли значение 10 мс в схеме грандера приложения? Стоит ли использовать неоптимальный интерфейс, который сложнее понять и поддерживать? Я не знаю ответа на эти вещи, но ответ не "абсолютно использовать разреженный массив независимо".
Джеффри Блатман
Ответы:
537
Кажется, я нашел решение. Я не правильно заметил keyAt(index)функцию.
Итак, я пойду с чем-то вроде этого:
for(int i =0; i < sparseArray.size(); i++){int key = sparseArray.keyAt(i);// get the object by the key.Object obj = sparseArray.get(key);}
в документации говорится, что «keyAt (int index), учитывая индекс в диапазоне 0 ... size () - 1, возвращает ключ из сопоставления ключ-значение indexth, которое хранит этот SparseArray». так что у меня работает нормально даже для описанного вами случая.
Рузанна
12
Лучше предварительно рассчитать размер массива и использовать постоянное значение в цикле.
Дмитрий Зайцев
25
Не проще ли здесь напрямую использовать функцию valueAt?
Милан Крстич
34
Это тоже будет работать внутри цикла:Object obj = sparseArray.valueAt(i);
Флориан,
27
valueAt(i)быстрее get(key), потому что valueAt(i)и keyAt(i)оба O (1) , но get(key)это O (log2 п) , так что я бы , конечно , всегда использовать valueAt.
Меки
180
Если вы не заботитесь о ключах, то valueAt(int)можете использовать их при переборе разреженного массива для прямого доступа к значениям.
for(int i =0, nsize = sparseArray.size(); i < nsize; i++){Object obj = sparseArray.valueAt(i);}
Использование valueAt () полезно (и быстрее, чем принятое решение), если ваша итерация не заботится о ключах, то есть: цикл, подсчитывающий вхождения определенного значения.
Соггер
2
Возьмите sparseArray.size()одну переменную, чтобы она не вызывала size()каждый раз.
Пратик Бутани
4
Копировать size () в переменную избыточно. Легко проверить, если вы просто посмотрите на код метода size (). Я не могу понять, почему вы не сделали, прежде чем предлагать такие вещи ... Я помню время 20 лет назад, когда у нас были простые связанные списки, которые действительно должны были подсчитывать их размер каждый раз, когда вы просили их об этом, но я не верю что такие вещи все еще существуют ...
Невероятный
Гарантируется ли это в ключевом порядке?
HughHughTeotl
18
Или просто создайте свой собственный 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();}}}
Для тех, кто использует Kotlin, честно говоря, самый простой способ перебора SparseArray: использовать расширение Kotlin от Anko или Android KTX ! (спасибо Yazazzello за указание на Android KTX)
да, вы на самом деле правы. мой плохой, я посмотрел на метки и подумал, что Котлина не должно быть здесь. Но теперь у меня возникли мысли, что этот ответ является хорошей ссылкой на самого Котлина. Хотя вместо использования Anko я бы рекомендовал использовать android.github.io/android-ktx/core-ktx (если вы любезно отредактируете свой ответ и добавите android-ktx, я его добавлю)
Yazazzello
@Yazazzello: эй, я даже не знал об Android KTX, хорошо!
0101100101
7
Для удаления всех элементов с SparseArrayпомощью приведенных выше циклов приводит к Exception.
Чтобы избежать этого, следуйте приведенному ниже коду, чтобы удалить все элементы из SparseArrayобычных циклов
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; в конце ничего не делает. Также есть метод, .clear()который следует назвать предпочтительным.
Пол Войташек
Почему вы используете цикл for () вместо while ()? То, что ты делаешь, не имеет смысла зацикливаться
Фил
Я предполагаю, что Сакурис хотела написать, i-=1;чтобы объяснить недостающий элемент. Но это лучше вернуться в цикл: for(int i=sparseArray.size()-1; i>=0; i++){...; илиwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
тыс
Ссылки типа «вышеприведенный цикл» вообще не имеют никакого смысла.
Невероятный Янв
Я думал, что целью итератора является безопасное удаление объектов. Я не видел ни одного примера класса Iterator с sparseArrays, как для хэш-карт. Это ближе всего подходит для безопасного удаления объектов, я надеюсь, что это работает без исключений одновременной модификации.
Androidcoder
5
Вот простые Iterator<T>и Iterable<T>реализации для SparseArray<T>:
Если вы используете Kotlin, вы можете использовать функции расширения как таковые, например:
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++))}}
Вы также можете преобразовать в список, если хотите. Пример:
sparseArray.keysIterator().asSequence().toList()
Я думаю, что может быть даже безопасно удалить элементы, используя сам removeпо LongSparseArrayсебе (не на итераторе), как это происходит в порядке возрастания.
РЕДАКТИРОВАТЬ: кажется, есть даже более простой способ, используя collection-ktx (пример здесь ). На самом деле это реализовано очень похоже на то, что я написал.
val sparse=LongSparseArray<String>()for(key in sparse.keyIterator()){}for(value in sparse.valueIterator()){}
sparse.forEach { key, value ->}
А для тех, кто использует Java, вы можете использовать LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratorи LongSparseArrayKt.forEach, к примеру. То же самое для других случаев.
Ответ - нет, потому SparseArrayчто не дает этого. Как pstговорится, эта штука не предоставляет никаких интерфейсов.
Вы можете зациклить 0 - size()и пропустить возвращаемые значения null, но это все.
Как я заявляю в своем комментарии, если вам нужно повторить, используйте Mapвместо SparseArray. Например, используйте a, TreeMapкоторый выполняет итерацию по ключу.
В принятом ответе есть дыры. Прелесть SparseArray состоит в том, что он допускает пробелы в индексах. Таким образом, у нас может быть две карты в SparseArray ...
(0,true)(250,true)
Обратите внимание, что здесь размер равен 2. Если мы итерируем по размеру, мы получим значения только для значений, сопоставленных с индексом 0 и индексом 1. Таким образом, отображение с ключом 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);}
Лучший способ сделать это - выполнить итерацию по размеру вашего набора данных, а затем проверить эти значения с помощью get () в массиве. Вот пример с адаптером, где я разрешаю групповое удаление элементов.
for(int index =0; index < mAdapter.getItemCount(); index++){if(toDelete.get(index)==true){long idOfItemToDelete =(allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);}}
Я думаю, что в идеале у семейства SparseArray должен быть метод getKeys (), но, увы, его нет.
Вы ошибаетесь - keyAtметод возвращает значение n-го ключа (в вашем примере keyAt(1)это вернуло бы 250), не путать с getкоторым возвращает значение элемента, на который ссылается ключ.
Eborbob
Я не уверен, что «это» в вашем комментарии. Вы признаете, что ваш ответ неверен, или вы говорите, что мой комментарий неверен? Если последнее, пожалуйста, проверьте developer.android.com/reference/android/util/
......
17
Мой ответ неверный, я не буду удалять его, чтобы другие могли учиться.
TreeMap<Integer, MyType>
что позволит вам итерировать в порядке по ключу. Как уже говорилось, SparseArray разработан, чтобы быть более эффективным, чем HashMap, но он не допускает итерации.Ответы:
Кажется, я нашел решение. Я не правильно заметил
keyAt(index)
функцию.Итак, я пойду с чем-то вроде этого:
источник
Object obj = sparseArray.valueAt(i);
valueAt(i)
быстрееget(key)
, потому чтоvalueAt(i)
иkeyAt(i)
оба O (1) , ноget(key)
это O (log2 п) , так что я бы , конечно , всегда использоватьvalueAt
.Если вы не заботитесь о ключах, то
valueAt(int)
можете использовать их при переборе разреженного массива для прямого доступа к значениям.источник
sparseArray.size()
одну переменную, чтобы она не вызывалаsize()
каждый раз.Или просто создайте свой собственный ListIterator:
источник
Просто как пирог. Просто убедитесь, что вы выбираете размер массива перед выполнением цикла.
Надеюсь это поможет.
источник
Для тех, кто использует Kotlin, честно говоря, самый простой способ перебора SparseArray: использовать расширение Kotlin от Anko или Android KTX ! (спасибо Yazazzello за указание на Android KTX)
Просто позвони
forEach { i, item -> }
источник
Для удаления всех элементов с
SparseArray
помощью приведенных выше циклов приводит кException
.Чтобы избежать этого, следуйте приведенному ниже коду, чтобы удалить все элементы из
SparseArray
обычных цикловисточник
.clear()
который следует назвать предпочтительным.i-=1;
чтобы объяснить недостающий элемент. Но это лучше вернуться в цикл:for(int i=sparseArray.size()-1; i>=0; i++){...
; илиwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
Вот простые
Iterator<T>
иIterable<T>
реализации дляSparseArray<T>
:Если вы хотите перебрать не только значение, но и ключ:
Полезно создавать служебные методы, которые возвращают
Iterable<T>
иIterable<SparseKeyValue<T>>
:Теперь вы можете повторить
SparseArray<T>
:источник
Если вы используете Kotlin, вы можете использовать функции расширения как таковые, например:
Вы также можете преобразовать в список, если хотите. Пример:
Я думаю, что может быть даже безопасно удалить элементы, используя сам
remove
поLongSparseArray
себе (не на итераторе), как это происходит в порядке возрастания.РЕДАКТИРОВАТЬ: кажется, есть даже более простой способ, используя collection-ktx (пример здесь ). На самом деле это реализовано очень похоже на то, что я написал.
Gradle требует этого:
Вот использование для LongSparseArray:
А для тех, кто использует Java, вы можете использовать
LongSparseArrayKt.keyIterator
,LongSparseArrayKt.valueIterator
иLongSparseArrayKt.forEach
, к примеру. То же самое для других случаев.источник
Ответ - нет, потому
SparseArray
что не дает этого. Какpst
говорится, эта штука не предоставляет никаких интерфейсов.Вы можете зациклить
0 - size()
и пропустить возвращаемые значенияnull
, но это все.Как я заявляю в своем комментарии, если вам нужно повторить, используйте
Map
вместоSparseArray
. Например, используйте a,TreeMap
который выполняет итерацию по ключу.источник
В принятом ответе есть дыры. Прелесть SparseArray состоит в том, что он допускает пробелы в индексах. Таким образом, у нас может быть две карты в SparseArray ...
Обратите внимание, что здесь размер равен 2. Если мы итерируем по размеру, мы получим значения только для значений, сопоставленных с индексом 0 и индексом 1. Таким образом, отображение с ключом 250 не доступно.
Лучший способ сделать это - выполнить итерацию по размеру вашего набора данных, а затем проверить эти значения с помощью get () в массиве. Вот пример с адаптером, где я разрешаю групповое удаление элементов.
Я думаю, что в идеале у семейства SparseArray должен быть метод getKeys (), но, увы, его нет.
источник
keyAt
метод возвращает значение n-го ключа (в вашем примереkeyAt(1)
это вернуло бы250
), не путать сget
которым возвращает значение элемента, на который ссылается ключ.