SparseArrayを反復処理する方法は?


311

Java SparseArray(Android用)を反復する方法はありますか?以前sparsearrayは簡単にインデックスで値を取得していました。見つかりませんでした。


30
うわー、完全に愛されていないクラスについて話し、ZEROコレクションインターフェイスに準拠しています...

1
TreeMap<Integer, MyType>キーを使用して順番に反復できるようにするa を使用できます。述べたように、SparseArrayはHashMapよりも効率的になるように設計されていますが、反復は許可されていません。
John B

2
選択したマップ実装のパフォーマンスがアプリのボトルネックになることはほとんどありません。
Jeffrey Blattman 2014

3
@JeffreyBlattmanは、明らかに適切な場合に、正しい構造の使用を避けるべきだという意味ではありません。
凍るような驚異的な

1
@frostymarvelousは、2倍の速さで、おそらく10ミリ秒未満の節約になると言っています。10msはアプリの壮大なスキームに関連していますか?理解と保守が難しい次善のインターフェースを使用する価値はありますか?これらの答えはわかりませんが、「絶対にスパース配列を使用する」というのは答えではありません。
Jeffrey Blattman、2016年

回答:


537

私は解決策を見つけたようです。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が格納するindex番目のキーと値のマッピングからキーが返される」と記載されています。ですから、あなたが説明した場合でも、私にとってはうまくいきます。
Ruzanna

12
配列のサイズを事前に計算し、ループで定数値を使用することをお勧めします。
Dmitry Zaytsev 2012

25
ここでvalueAt関数を直接使用する方が簡単ではないでしょうか?
ミラノクルスティック2013

34
これは、ループ内であまりにも動作します:Object obj = sparseArray.valueAt(i);
フロリアン

27
valueAt(i)とはどちらもO(1)ですが、O(log2 n)であるためget(key)、より高速です。そのためvalueAt(i)、常にを使用します。keyAt(i)get(key)valueAt
Mecki 2015

180

キーを気にしない場合はvalueAt(int)、スパース配列を反復処理して値に直接アクセスするときに使用できます。

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

7
反復がキーを気にしない場合、つまり、特定の値の発生をカウントするループの場合、valueAt()の使用は便利です(受け入れられているソリューションよりも高速です)。
Sogger 2013年

2
sparseArray.size()1つの変数を取り、size()毎回呼び出されないようにします。
Pratik Butani 16年

4
size()を変数にコピーすることは冗長ではありません。size()メソッドのコードを見れば簡単に確認できます。あなたがそのようなことを提案する前になぜあなたがそうしなかったのか理解できません...あなたがそれを要求するたびに実際にサイズを数える必要がある単純なリンクリストがあった20年前のことを覚えていますが、私は信じていませんそのようなものがまだ存在していること...
信じられないほどのJan

これはキーの順序であることが保証されていますか?
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
私見それは少しオーバーエンジニアリングのようです。それは素晴らしいです
hrules6872

12

パイのようにシンプル。実際にループを実行するに、必ず配列サイズをフェッチしてください。

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

お役に立てれば。


11

Kotlinを使用している誰のために、正直にSparseArrayを反復処理するためにはるかに最も簡単な方法は次のとおりです。使用からKotlin拡張子あんこAndroidのKTX!(Android KTXを指摘したYazazzelloへのクレジット)

単に電話する forEach { i, item -> }


うん、あなたは実際に正しいです。私の悪いことに、私はタグを見て、コトリンがここにいるべきではないと思いました。しかし今、この答えはKotlin自体への良い参照であるという再考があります。Ankoを使用する代わりに、android.github.io / android-ktx / core-ktxを使用することをお勧めします(回答を親切に編集してandroid-ktxを追加できれば、賛成投票します)
Yazazzello

@Yazazzelloちょっと私はAndroid 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 16年

なぜ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);...
2017年

「上記のループ」のような参照はまったく意味がありません。
信じられない月

「イテレータ」のポイントは、オブジェクトを安全に削除することだと思いました。ハッシュマップのようにsparseArraysを持つIteratorクラスの例を見たことがありません。これは、安全なオブジェクトの削除への対処に最も近くなります。同時変更の例外なしで機能することを願っています。
Androidcoder 2018年

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

昇順なのでremoveLongSparseArrayそれ自体(イテレーターではなく)を使用してアイテムを削除しても安全だと思います。


編集: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.valueIteratorおよびを使用できますLongSparseArrayKt.forEach。他の場合も同じです。


-5

SparseArray提供しないので答えはノーです。つまりpst、これはインターフェースを提供していません。

からループし0 - size()て値を返す値をスキップすることもできますnullが、それはそれだけです。

コメントで述べたように、反復する必要がある場合は、のMap代わりにを使用してくださいSparseArray。たとえばTreeMap、キーで順番に反復するa を使用します。

TreeMap<Integer, MyType>

-6

受け入れられた答えにはいくつかの穴があります。SparseArrayの優れた点は、インデックスのギャップを許容することです。したがって、SparseArrayに次のような2つのマップを含めることができます...

(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)はが返されます250)。getこれは、キーが参照する要素の値を返すのと混同しないでください。
エボルボブ2015年

コメントの「これ」が何であるかわかりません。あなたの答えが間違っていることを認めていますか、それとも私のコメントが間違っていると言っていますか?後者の場合は、developer.android.com
reference / android / util /…

17
私の答えは間違っています。他の人が学ぶことができるようにそれを削除しません。
タイラーPfaff
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.