Java SparseArray(Android用)を反復する方法はありますか?以前sparsearray
は簡単にインデックスで値を取得していました。見つかりませんでした。
TreeMap<Integer, MyType>
キーを使用して順番に反復できるようにするa を使用できます。述べたように、SparseArrayはHashMapよりも効率的になるように設計されていますが、反復は許可されていません。
Java SparseArray(Android用)を反復する方法はありますか?以前sparsearray
は簡単にインデックスで値を取得していました。見つかりませんでした。
TreeMap<Integer, MyType>
キーを使用して順番に反復できるようにするa を使用できます。述べたように、SparseArrayはHashMapよりも効率的になるように設計されていますが、反復は許可されていません。
回答:
私は解決策を見つけたようです。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);
}
Object obj = sparseArray.valueAt(i);
valueAt(i)
とはどちらもO(1)ですが、O(log2 n)であるためget(key)
、より高速です。そのためvalueAt(i)
、常にを使用します。keyAt(i)
get(key)
valueAt
キーを気にしない場合はvalueAt(int)
、スパース配列を反復処理して値に直接アクセスするときに使用できます。
for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
Object obj = sparseArray.valueAt(i);
}
sparseArray.size()
1つの変数を取り、size()
毎回呼び出されないようにします。
独自の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();
}
}
}
Kotlinを使用している誰のために、正直にSparseArrayを反復処理するためにはるかに最も簡単な方法は次のとおりです。使用からKotlin拡張子あんこやAndroidのKTX!(Android KTXを指摘したYazazzelloへのクレジット)
単に電話する forEach { i, item -> }
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;
}
}
.clear()
があります。
i-=1;
が今欠けている要素を説明するために書きたかったと思います。ただし、ループを元に戻すことをお勧めしますfor(int i=sparseArray.size()-1; i>=0; i++){...
。またはwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
ここではシンプルである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)) {
// ...
}
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
。他の場合も同じです。
受け入れられた答えにはいくつかの穴があります。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()メソッドがあるのが理想的ですが、残念ながらありません。
keyAt
メソッドはn番目のキーの値を返します(この例でkeyAt(1)
はが返されます250
)。get
これは、キーが参照する要素の値を返すのと混同しないでください。