如何遍历SparseArray?

Ruz*_*nna 302 java android

有没有办法迭代Java SparseArray(适用于Android)?我sparsearray以前很容易通过索引获取值.我找不到一个.

Ruz*_*nna 525

似乎我找到了解决方案.我没有正确地注意到这个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);
}
Run Code Online (Sandbox Code Playgroud)

  • 这在循环中也会起作用:`Object obj = sparseArray.valueAt(i);` (33认同)
  • 文档声明"keyAt(int index)给定范围为0 ... size() - 1的索引,返回此SparseArray存储的indexth键值映射中的键." 因此,即使对于您所描述的情况,它也能正常工作. (25认同)
  • 在这里直接使用valueAt函数会不会更容易? (25认同)
  • `valueAt(i)`比`get(key)`更快,因为`valueAt(i)`和`keyAt(i)`都是**O(1)**,但`get(key)`是**O(log2 n)**,所以我当然总是使用`valueAt`. (25认同)
  • 最好预先计算数组的大小并在循环中使用常量值. (12认同)
  • 有趣的是,我们都错过了这个,大概是因为我们正在寻找通常的收集界面方法......这就是它. (6认同)
  • 这实际上工作正常.请记住,"i"不是您用于插入项目的真实索引.这是"关键". (3认同)
  • 在SparseArray中,位置2只有一个项目(例如:`{null,null,Object}`),`sparseArray.size()== 1`但是`sparseArray.valueAt(0)`为null.但是,`sparseArray.keyAt(0)`返回第一个键,它是2,因此使用`sparseArray.valueAt(sparseArray.keyAt(i))`比接受的答案更安全. (3认同)
  • 看一下size()的实现,它只是检查gc并返回预先计算的mSize变量。无需过度优化。 (2认同)

小智 179

如果您不关心密钥,则valueAt(int)可以在迭代稀疏数组时直接访问值.

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

  • 如果迭代不关心键,则使用valueAt()是有用的(并且比接受的解决方案更快),即:计算特定值的出现的循环. (7认同)
  • 将size()复制到变量是多余的.如果你只是查看size()方法的代码,很容易检查.在你提出这样的事情之前我无法理解为什么你没有...我记得20年前的一个时间我们有简单的链接列表,每当你问它时它们真的必须计算它们的大小但是我不相信这样的事情仍然存在...... (4认同)
  • 在一个变量中使用`sparseArray.size()`,因此每次都不会调用`size()`. (2认同)

010*_*101 18

您只需创建自己的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();
    }
}
}
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,它似乎有点过度工程.这太棒了 (8认同)

Pas*_*cal 11

像饼一样简单.只需确保实际执行循环之前获取数组大小.

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

希望这可以帮助.


010*_*101 10

无论谁使用Kotlin,老实说,迭代SparseArray的最简单方法是:使用AnkoAndroid KTX的Kotlin扩展!(感谢Yazazzello指出Android KTX)

只需致电 forEach { i, item -> }


Sac*_*ise 7

用于删除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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • i = -1; 最后什么也没做.还有一个名为`.clear()`的方法应该受到青睐. (2认同)

mix*_*xel 5

这是简单Iterator<T>Iterable<T>实现的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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您不仅要迭代值而且要迭代密钥:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

创建返回的实用程序方法很有用,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);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

SparseArray<String> a = ...;

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

for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
  // ...
}
Run Code Online (Sandbox Code Playgroud)


and*_*per 5

如果您使用 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++))
    }
}
Run Code Online (Sandbox Code Playgroud)

如果您愿意,也可以转换为列表。例子:

sparseArray.keysIterator().asSequence().toList()
Run Code Online (Sandbox Code Playgroud)

我认为它甚至可能是安全删除使用项目removeLongSparseArray本身(而不是在迭代器),因为它是按升序排列。


编辑:似乎有一种更简单的方法,通过使用collection-ktx (example here )。实际上,它的实现方式与我所写的非常相似。

Gradle 要求:

implementation 'androidx.core:core-ktx:#'
implementation 'androidx.collection:collection-ktx:#'
Run Code Online (Sandbox Code Playgroud)

这是 LongSparseArray 的用法:

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

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