如何通过 SparseArray 来迭代数据?

是否有一种方法来迭代 Java SparseArray (Android) ? 我使用 sparsearray 方便地按索引获取值。但是我找不到对应的方法。

108817 次浏览

答案是否定的,因为SparseArray没有提供。正如pst所说,这个东西不提供任何接口。

你可以从0 - size()开始循环,并跳过返回null的值,但仅此而已。

正如我在评论中所述,如果需要迭代,请使用Map而不是SparseArray。例如,使用TreeMap,它按键的顺序迭代。

TreeMap<Integer, MyType>

看来我找到解决办法了。我没有正确地注意到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);
}

如果你不关心键,那么可以在遍历稀疏数组时使用valueAt(int)来直接访问值。

for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
Object obj = sparseArray.valueAt(i);
}

或者你只需要创建你自己的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();
}
}
}

公认的答案有一些漏洞。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()方法,但可惜它没有。

简单得很。只要确保你获取数组大小之前实际上执行循环。

for(int i = 0, arraySize= mySparseArray.size(); i < arraySize; i++) {
Object obj = mySparseArray.get(/* int key = */ mySparseArray.keyAt(i));
}

希望这能有所帮助。

使用上述循环从SparseArray中删除所有元素会导致Exception

为了避免这种情况,按照下面的代码使用普通循环删除SparseArray中的所有元素

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;
}
}

下面是SparseArray<T>的简单Iterator<T>Iterable<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);
}
}

如果你不仅要迭代一个值,还要迭代一个键:

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);
}
}

创建返回Iterable<T>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);
}
}

现在你可以迭代SparseArray<T>:

SparseArray<String> a = ...;


for (String s: SparseArrayUtils.iterable(a)) {
// ...
}


for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
// ...
}

对于使用Kotlin的人来说,老实说,迭代SparseArray最简单的方法是:从Android KTX使用Kotlin扩展!(感谢Yazazzello指出Android KTX)

简单地调用forEach { i, item -> }

如果你使用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()

我认为在LongSparseArray本身(而不是迭代器)上使用remove删除项可能是安全的,因为它是升序的。


编辑:似乎有一个更简单的方法,使用< >强collection-ktx < / >强(示例这里<强> < / >强)。实际上,它的实现方式与我写的非常相似。

Gradle要求:

implementation 'androidx.core:core-ktx:#'
implementation 'androidx.collection:collection-ktx:#'

下面是LongSparseArray的用法:

    val sparse= LongSparseArray<String>()
for (key in sparse.keyIterator()) {
}
for (value in sparse.valueIterator()) {
}
sparse.forEach { key, value ->
}

而对于那些使用Java的,你可以使用LongSparseArrayKt.keyIteratorLongSparseArrayKt.valueIteratorLongSparseArrayKt.forEach,例如。其他情况也一样。