How to iterate through SparseArray?

后端 未结 10 1725
野的像风
野的像风 2020-11-30 18:16

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.

相关标签:
10条回答
  • 2020-11-30 18:42

    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();
        }
    }
    }
    
    0 讨论(0)
  • 2020-11-30 18:45

    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)) {
      // ...
    }
    
    0 讨论(0)
  • 2020-11-30 18:52

    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>
    
    0 讨论(0)
  • 2020-11-30 18:54

    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.

    0 讨论(0)
提交回复
热议问题