Is there a way to iterate over Java SparseArray (for Android) ? I used sparsearray
to easily get values by index. I could not find one.
Seems I found the solution. I hadn't properly noticed the keyAt(index)
function.
So I'll go with something like this:
for(int i = 0; i < sparseArray.size(); i++) {
int key = sparseArray.keyAt(i);
// get the object by the key.
Object obj = sparseArray.get(key);
}
Object obj = sparseArray.valueAt(i);
–
Like valueAt(i)
is faster than get(key)
, because valueAt(i)
and keyAt(i)
are both O(1), but get(key)
is O(log2 n), so I would certainly always use valueAt
. –
Prosimian {null, null, Object}
), sparseArray.size() == 1
but sparseArray.valueAt(0)
is null. However, sparseArray.keyAt(0)
returns the first key, which is 2, so it's safer to use sparseArray.valueAt(sparseArray.keyAt(i))
like the accepted answer does. –
Naseby sparseArray.size()
in one variable so it will not call size()
every time. –
Topgallant valueAt
. Feel free to upvote it. –
Spiv valueAt(i)
returns the same as calling get(keyAt(i))
. Further, you would really never want to pass the result of a call to keyAt()
into a call to valueAt()
, which you did in your last code expression. –
Poundage SparseArray
, because SparseArray
doesn't implement the Iterable
interface. SparseArray
also doesn't seem to have keySet()
or entrySet()
methods the way standard Map
s do. So the only way to iterate is using the keyAt()
and valueAt()/get()
methods. –
Poundage If you don't care about the keys, then valueAt(int)
can be used to while iterating through the sparse array to access the values directly.
for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
Object obj = sparseArray.valueAt(i);
}
sparseArray.size()
in one variable so it will not call size()
every time. –
Topgallant Ooor you just create your own ListIterator:
public final class SparseArrayIterator<E> implements ListIterator<E> {
private final SparseArray<E> array;
private int cursor;
private boolean 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)}.
*/
public static <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)}.
*/
public static <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)}.
*/
public static <E> ListIterator<E> iterateAt(SparseArray<E> array, int location) {
return new SparseArrayIterator<E>(array, location);
}
private SparseArrayIterator(SparseArray<E> array, int location) {
this.array = array;
if (location < 0) {
cursor = -1;
cursorNowhere = true;
} else if (location < array.size()) {
cursor = location;
cursorNowhere = false;
} else {
cursor = array.size() - 1;
cursorNowhere = true;
}
}
@Override
public boolean hasNext() {
return cursor < array.size() - 1;
}
@Override
public boolean hasPrevious() {
return cursorNowhere && cursor >= 0 || cursor > 0;
}
@Override
public int nextIndex() {
if (hasNext()) {
return array.keyAt(cursor + 1);
} else {
throw new NoSuchElementException();
}
}
@Override
public int previousIndex() {
if (hasPrevious()) {
if (cursorNowhere) {
return array.keyAt(cursor);
} else {
return array.keyAt(cursor - 1);
}
} else {
throw new NoSuchElementException();
}
}
@Override
public E next() {
if (hasNext()) {
if (cursorNowhere) {
cursorNowhere = false;
}
cursor++;
return array.valueAt(cursor);
} else {
throw new NoSuchElementException();
}
}
@Override
public E previous() {
if (hasPrevious()) {
if (cursorNowhere) {
cursorNowhere = false;
} else {
cursor--;
}
return array.valueAt(cursor);
} else {
throw new NoSuchElementException();
}
}
@Override
public void add(E object) {
throw new UnsupportedOperationException();
}
@Override
public void remove() {
if (!cursorNowhere) {
array.remove(array.keyAt(cursor));
cursorNowhere = true;
cursor--;
} else {
throw new IllegalStateException();
}
}
@Override
public void set(E object) {
if (!cursorNowhere) {
array.setValueAt(cursor, object);
} else {
throw new IllegalStateException();
}
}
}
Simple as Pie. Just make sure you fetch array size before actually performing the loop.
for(int i = 0, arraySize= mySparseArray.size(); i < arraySize; i++) {
Object obj = mySparseArray.get(/* int key = */ mySparseArray.keyAt(i));
}
Hope this helps.
For whoever is using Kotlin, honestly the by far easiest way to iterate over a SparseArray is: Use the Kotlin extension from Anko or Android KTX! (credit to Yazazzello for pointing out Android KTX)
Simply call forEach { i, item -> }
For removing all the elements from SparseArray
using the above looping leads to Exception
.
To avoid this Follow the below code to remove all the elements from SparseArray
using normal loops
private void 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;
}
}
.clear()
which should be favored. –
Corruption i-=1;
to account for the now missing element. But it's better to revert the loop: for(int i=sparseArray.size()-1; i>=0; i++){...
; or while (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
–
Dorothy Here is simple Iterator<T>
and Iterable<T>
implementations for SparseArray<T>
:
public class SparseArrayIterator<T> implements Iterator<T> {
private final SparseArray<T> array;
private int index;
public SparseArrayIterator(SparseArray<T> array) {
this.array = array;
}
@Override
public boolean hasNext() {
return array.size() > index;
}
@Override
public T next() {
return array.valueAt(index++);
}
@Override
public void remove() {
array.removeAt(index);
}
}
public class SparseArrayIterable<T> implements Iterable<T> {
private final SparseArray<T> sparseArray;
public SparseArrayIterable(SparseArray<T> sparseArray) {
this.sparseArray = sparseArray;
}
@Override
public Iterator<T> iterator() {
return new SparseArrayIterator<>(sparseArray);
}
}
If you want to iterate not only a value but also a key:
public class SparseKeyValue<T> {
private final int key;
private final T value;
public SparseKeyValue(int key, T value) {
this.key = key;
this.value = value;
}
public int getKey() {
return key;
}
public T getValue() {
return value;
}
}
public class SparseArrayKeyValueIterator<T> implements Iterator<SparseKeyValue<T>> {
private final SparseArray<T> array;
private int index;
public SparseArrayKeyValueIterator(SparseArray<T> array) {
this.array = array;
}
@Override
public boolean hasNext() {
return array.size() > index;
}
@Override
public SparseKeyValue<T> next() {
SparseKeyValue<T> keyValue = new SparseKeyValue<>(array.keyAt(index), array.valueAt(index));
index++;
return keyValue;
}
@Override
public void remove() {
array.removeAt(index);
}
}
public class SparseArrayKeyValueIterable<T> implements Iterable<SparseKeyValue<T>> {
private final SparseArray<T> sparseArray;
public SparseArrayKeyValueIterable(SparseArray<T> sparseArray) {
this.sparseArray = sparseArray;
}
@Override
public Iterator<SparseKeyValue<T>> iterator() {
return new SparseArrayKeyValueIterator<T>(sparseArray);
}
}
It's useful to create utility methods that return Iterable<T>
and Iterable<SparseKeyValue<T>>
:
public abstract class SparseArrayUtils {
public static <T> Iterable<SparseKeyValue<T>> keyValueIterable(SparseArray<T> sparseArray) {
return new SparseArrayKeyValueIterable<>(sparseArray);
}
public static <T> Iterable<T> iterable(SparseArray<T> sparseArray) {
return new SparseArrayIterable<>(sparseArray);
}
}
Now you can iterate SparseArray<T>
:
SparseArray<String> a = ...;
for (String s: SparseArrayUtils.iterable(a)) {
// ...
}
for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
// ...
}
If you use Kotlin, you can use extension functions as such, for example:
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++))
}
}
You can also convert to a list, if you wish. Example:
sparseArray.keysIterator().asSequence().toList()
I think it might even be safe to delete items using remove
on the LongSparseArray
itself (not on the iterator), as it is in ascending order.
EDIT: Seems there is even an easier way, by using collection-ktx (example here) . It's implemented in a very similar way to what I wrote, actally.
Gradle requires this:
implementation 'androidx.core:core-ktx:#'
implementation 'androidx.collection:collection-ktx:#'
Here's the usage for LongSparseArray :
val sparse= LongSparseArray<String>()
for (key in sparse.keyIterator()) {
}
for (value in sparse.valueIterator()) {
}
sparse.forEach { key, value ->
}
And for those that use Java, you can use LongSparseArrayKt.keyIterator
, LongSparseArrayKt.valueIterator
and LongSparseArrayKt.forEach
, for example. Same for the other cases.
The answer is no because SparseArray
doesn't provide it. As pst
put it, this thing doesn't provide any interfaces.
You could loop from 0 - size()
and skip values that return null
, but that is about it.
As I state in my comment, if you need to iterate use a Map
instead of a SparseArray
. For example, use a TreeMap
which iterates in order by the key.
TreeMap<Integer, MyType>
The accepted answer has some holes in it. The beauty of the SparseArray is that it allows gaps in the indeces. So, we could have two maps like so, in a SparseArray...
(0,true)
(250,true)
Notice the size here would be 2. If we iterate over size, we will only get values for the values mapped to index 0 and index 1. So the mapping with a key of 250 is not accessed.
for(int i = 0; i < sparseArray.size(); i++) {
int key = sparseArray.keyAt(i);
// get the object by the key.
Object obj = sparseArray.get(key);
}
The best way to do this is to iterate over the size of your data set, then check those indeces with a get() on the array. Here is an example with an adapter where I am allowing batch delete of items.
for (int index = 0; index < mAdapter.getItemCount(); index++) {
if (toDelete.get(index) == true) {
long idOfItemToDelete = (allItems.get(index).getId());
mDbManager.markItemForDeletion(idOfItemToDelete);
}
}
I think ideally the SparseArray family would have a getKeys() method, but alas it does not.
keyAt
method returns the value of the nth key (in your example keyAt(1)
would return 250
), not to be confused with get
which returns the value of the element referenced by the key. –
Service © 2022 - 2024 — McMap. All rights reserved.
TreeMap<Integer, MyType>
which would allow you to iterate in order by key. As stated, SparseArray is designed to be more efficient than a HashMap, but it doesn't allow iteration. – Instrumentality