SparseArray를 반복하는 방법?


311

Java SparseArray (Android 용)를 반복하는 방법이 있습니까? 나는 sparsearray인덱스로 값을 쉽게 얻었습니다. 찾을 수 없습니다.


30
와우, 약 이야기 완전히 사랑을받지 못하는 클래스 , ZERO 수집 인터페이스에 부합 ...

1
TreeMap<Integer, MyType>키를 사용하여 순서대로 반복 할 수 있는 a 를 사용할 수 있습니다 . 언급했듯이 SparseArray는 HashMap보다 효율적으로 설계되었지만 반복을 허용하지 않습니다.
John B

2
선택한지도 성능의 성능이 앱의 병목 현상이 될 가능성은 거의 없습니다.
Jeffrey Blattman

3
@JeffreyBlattman은 그것이 적절할 때 올바른 구조를 사용하지 않아야한다는 것을 의미하지는 않습니다.
frostymarvelous

1
@frostymarvelous는 두 배나 빠르다고 말하는데, 아마도 10ms 미만의 절약을 의미 할 것입니다. 10ms는 앱의 더 큰 계획과 관련이 있습니까? 이해하고 유지하기 어려운 최적의 인터페이스를 사용하는 것이 가치가 있습니까? 나는 그 것들에 대한 답을 모르지만 그 대답은 "절대적으로 희소 배열을 사용하지 않습니다".
Jeffrey Blattman

답변:


536

해결책을 찾은 것 같습니다. 나는 그 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);
}

25
문서에 "keyAt (int index) 범위 0 ... size ()-1의 인덱스가 주어지면이 SparseArray가 저장하는 인덱스 키-값 매핑에서 키를 반환합니다." 당신이 묘사 한 경우에도 잘 작동합니다.
Ruzanna

12
배열의 크기를 미리 계산하고 루프에서 상수 값을 사용하는 것이 좋습니다.
Dmitry Zaytsev

25
직접 valueAt 함수를 사용하는 것이 더 쉽지 않습니까?
Milan Krstic

34
이것은 루프 Object obj = sparseArray.valueAt(i);
Florian

27
valueAt(i)보다 빠른입니다 get(key)때문에, valueAt(i)그리고 keyAt(i)모두 O (1) , 그러나 get(key)입니다 O (LOG2 N) 나는 확실히 항상 사용합니다, 그래서 valueAt.
Mecki

180

키를 신경 쓰지 않으면 valueAt(int)스파 스 배열을 반복하면서 값을 직접 액세스하는 데 사용할 수 있습니다.

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

7
반복이 키에 관심이없는 경우 (예 : 특정 값의 루프 카운트 발생) valueAt ()을 사용하면 유용합니다 (허용 된 솔루션보다 빠름).
Sogger

2
가져 sparseArray.size()가 호출되지 않도록 하나 개의 변수에 size()모든 시간을.
Pratik Butani

4
size ()를 변수에 복사하는 것은 중복되지 않습니다. size () 메서드의 코드 만 보면 쉽게 확인할 수 있습니다. 나는 당신이 그런 것들을 제안하기 전에 왜 당신이하지 않았는지 이해할 수 없습니다 ... 20 년 전 우리가 당신이 그것을 요청할 때마다 실제로 크기를 계산 해야하는 간단한 링크 된 목록을 가지고 있었던 시간을 기억합니다. 그런 것들이 여전히 존재합니다 ...
놀라운 1

이것이 핵심 순서로 보장됩니까?
HughHughTeotl

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

9
IMHO 그것은 약간 과잉 엔지니어링 된 것 같습니다. 그것은 멋진
hrules6872

12

파이처럼 간단합니다. 실제로 루프를 수행 하기 전에 배열 크기를 가져와야 합니다.

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

도움이 되었기를 바랍니다.


11

Kotlin을 사용하는 사람이라면 SparseArray를 반복하는 가장 쉬운 방법은 다음과 같습니다. Anko 또는 Android KTX 의 Kotlin 확장 프로그램을 사용하십시오 ! (Android KTX를 지적하는 Yazazzello의 신용)

간단히 전화 forEach { i, item -> }


네, 당신은 실제로 옳습니다. 내 나쁜, 나는 태그를보고 코 틀린이 여기에 있으면 안된다고 생각했다. 그러나 이제이 답변이 Kotlin 자체에 대한 좋은 참조라고 생각합니다. Anko를 사용하는 대신 android.github.io/android-ktx/core-ktx 를 사용하는 것이 좋습니다. 답변을 친절하게 편집하고 android-ktx를 추가 할 수 있다면 투표하겠습니다.
Yazazzello

@ Yazazzello 안녕하세요, 안드로이드 KTX에 대해서도 몰랐습니다.
0101100101

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

2
i = -1; 결국에는 아무것도하지 않습니다. 또한 .clear()선호되는 방법 이 있습니다.
Paul Woitaschek

while () 대신 for () 루프를 사용하는 이유는 무엇입니까? 당신이하고있는 일은 루핑에는 의미가 없습니다
Phil A

나는 Sackurise가 i-=1;현재 누락 된 요소를 설명 하기 위해 쓰려고한다고 가정 합니다. 그러나 루프를 되 돌리는 것이 좋습니다. for(int i=sparseArray.size()-1; i>=0; i++){...; 또는while (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
THS

"위의 루핑"과 같은 참조는 전혀 의미가 없습니다.
놀라운 1

'반복자'의 요점은 안전한 객체 제거라고 생각했습니다. 해시 맵에 대한 것처럼 sparseArrays가있는 Iterator 클래스의 예를 보지 못했습니다. 이것은 안전한 객체 제거 문제를 해결하는 데 가장 가깝습니다. 동시 수정 예외없이 작동하기를 바랍니다.
Androidcoder

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

값뿐만 아니라 키를 반복하려면 다음을 수행하십시오.

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)) {
  // ...
}

4

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 ( 여기 예제 ) 를 사용하면 더 쉬운 방법이있는 것 같습니다 . 실제로 작성한 것과 매우 유사한 방식으로 구현되었습니다.

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.keyIterator, LongSparseArrayKt.valueIterator및을 사용할 수 있습니다 LongSparseArrayKt.forEach. 다른 경우도 마찬가지입니다.


-5

대답을 SparseArray제공 하지 않기 때문에 대답은 아니오 입니다. 요컨대 pst, 이것은 인터페이스를 제공하지 않습니다.

0 - size()을 반환하는 값을 반복 하거나 건너 뛸 수 null있지만 그 정도입니다.

내 의견에서 언급했듯이 반복 해야하는 경우 Map대신을 사용하십시오 SparseArray. 예를 들어, TreeMap키를 기준으로 반복되는 a 를 사용하십시오 .

TreeMap<Integer, MyType>

-6

허용되는 답변에는 약간의 구멍이 있습니다. SparseArray의 장점은 indeces에 차이가 있다는 것입니다. 그래서 우리는 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 () 메소드가 있지만 아쉽지 않습니다.


4
잘못되었습니다.이 keyAt방법은 n 번째 키의 값을 반환합니다 (이 예제 keyAt(1)에서는 return 250). 혼동하지 말고 get키가 참조하는 요소의 값을 반환하십시오.
Eborbob

귀하의 의견에 '이것'이 무엇인지 잘 모르겠습니다. 당신은 당신의 대답이 틀렸다는 것을 인정하고 있습니까? 후자가 developer.android.com/reference/android/util/…
Eborbob

17
내 답변이 잘못되었습니다. 다른 사람이 배울 수 있도록 삭제하지 않습니다.
Tyler Pfaff
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.