317

Có cách nào để lặp qua Java SparseArray (dành cho Android) không? Tôi đã từng sparsearraydễ dàng nhận được các giá trị theo chỉ mục. Tôi không thể tìm thấy một.

|
  • 30
    Wow, hãy nói về một lớp học hoàn toàn không được yêu thương , chiếu theo giao diện bộ sưu tập ZERO ... –  17:26:51 03/11/2011
  • 1
    Bạn có thể sử dụng một TreeMap<Integer, MyType>mà sẽ cho phép bạn lặp lại theo thứ tự từng khóa. Như đã nói, SparseArray được thiết kế để hiệu quả hơn HashMap, nhưng nó không cho phép lặp lại. –  17:43:57 03/11/2011
  • 2
    rất, rất khó xảy ra rằng hiệu suất của bản đồ mà bạn chọn sẽ trở thành nút thắt cổ chai trong ứng dụng của bạn. –  16:49:09 09/07/2014
  • 3
    @JeffreyBlattman không có nghĩa là chúng ta nên tránh sử dụng đúng cấu trúc khi nó rõ ràng là phù hợp. –  08:41:36 06/05/2016
  • 1
    @frostymarvelous nói rằng TWICE nhanh như vậy, điều đó có thể có nghĩa là tiết kiệm được dưới 10 mili giây. 10ms có liên quan trong sơ đồ lớn hơn của ứng dụng không? Việc sử dụng một giao diện tối ưu phụ khó hiểu và khó bảo trì có đáng không? Tôi không biết câu trả lời cho những điều đó, nhưng câu trả lời không phải là "tuyệt đối sử dụng mảng thưa thớt bất chấp". –  15:55:15 06/05/2016
544

Có vẻ như tôi đã tìm thấy giải pháp. Tôi đã không nhận thấy đúng keyAt(index)chức năng.

Vì vậy, tôi sẽ đi với một cái gì đó như sau:

for(int i = 0; i < sparseArray.size(); i++) {
   int key = sparseArray.keyAt(i);
   // get the object by the key.
   Object obj = sparseArray.get(key);
}
|
  • 1
    tài liệu nói rằng "keyAt (int index) Cho một chỉ mục trong phạm vi 0 ... size () - 1, trả về khóa từ ánh xạ khóa-giá trị chỉ mục mà SparseArray này lưu trữ." vì vậy nó hoạt động tốt cho tôi ngay cả đối với trường hợp do bạn mô tả. –  17:13:52 03/03/2012
  • 1
    tốt hơn nên tính toán trước kích thước của mảng và sử dụng giá trị không đổi trong vòng lặp. –  13:55:17 06/09/2012
  • 1
    Sử dụng trực tiếp hàm valueAt ở đây có dễ dàng hơn không? –  23:25:44 06/02/2013
  • 1
    Điều này cũng sẽ hoạt động bên trong vòng lặp:Object obj = sparseArray.valueAt(i); –  17:01:37 15/02/2013
  • 1
    valueAt(i)nhanh hơn get(key), bởi vì valueAt(i)keyAt(i)cả hai đều là O (1) , nhưng get(key)O (log2 n) , vì vậy tôi chắc chắn sẽ luôn sử dụng valueAt. –  11:42:04 23/02/2015
183

Nếu bạn không quan tâm đến các khóa, thì bạn valueAt(int)có thể sử dụng để lặp qua mảng thưa thớt để truy cập trực tiếp các giá trị.

for(int i = 0, nsize = sparseArray.size(); i < nsize; i++) {
    Object obj = sparseArray.valueAt(i);
}
|
  • 1
    Sử dụng valueAt () hữu ích (và nhanh hơn giải pháp được chấp nhận) nếu quá trình lặp lại của bạn không quan tâm đến các khóa, tức là: số lần đếm vòng lặp của một giá trị cụ thể. –  19:30:05 13/03/2013
  • 1
    Đưa sparseArray.size()vào một biến để nó sẽ không gọi size()mọi lúc. –  04:37:53 26/10/2016
  • 1
    Sẽ là thừa nếu sao chép size () vào một biến. Dễ dàng kiểm tra nếu bạn chỉ nhìn vào mã của phương thức size (). Tôi không thể hiểu tại sao bạn không đề xuất những điều như vậy trước khi bạn đề xuất những điều như vậy ... Tôi nhớ khoảng thời gian 20 năm trước, nơi chúng tôi có danh sách liên kết đơn giản mà thực sự phải đếm kích thước của chúng mỗi khi bạn yêu cầu nhưng tôi không tin rằng những việc như vậy vẫn còn tồn tại ... –  08:38:19 23/02/2018
  • 1
    Điều này có được đảm bảo theo thứ tự chính không? –  20:53:03 27/04/2019
18

Ngoài ra, bạn chỉ cần tạo ListIterator của riêng mình:

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

Đơn giản như Pie. Chỉ cần đảm bảo rằng bạn tìm nạp kích thước mảng trước khi thực sự thực hiện vòng lặp.

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

Hi vọng điêu nay co ich.

|
12

Đối với bất kỳ ai đang sử dụng Kotlin, thành thật mà nói, cách dễ nhất để lặp qua SparseArray là: Sử dụng tiện ích mở rộng Kotlin từ Anko hoặc Android KTX ! (tín dụng cho Yazazzello vì đã chỉ ra Android KTX)

Đơn giản chỉ cần gọi forEach { i, item -> }

|
  • 1
    vâng, bạn thực sự đúng. tệ của tôi, tôi nhìn vào các thẻ và nghĩ rằng Kotlin không nên ở đây. Nhưng bây giờ suy nghĩ thứ hai rằng câu trả lời này là một tham chiếu tốt cho chính Kotlin. Mặc dù thay vì sử dụng Anko tôi khuyên bạn nên sử dụng android.github.io/android-ktx/core-ktx (nếu bạn vui lòng có thể chỉnh sửa câu trả lời của bạn và thêm android-KTX tôi sẽ upvote nó) –  10:01:48 19/02/2018
  • 1
    @Yazazzello này, tôi thậm chí còn không biết về KTX Android, điểm tốt! –  01:08:36 20/02/2018
7

Đối với việc loại bỏ tất cả các phần tử khỏi SparseArrayviệc sử dụng vòng lặp trên dẫn đến Exception.

Để tránh điều này Làm theo mã dưới đây để loại bỏ tất cả các phần tử khỏi SparseArraycác vòng lặp thông thường

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;
    }
}
|
  • 1
    Các i = -1; cuối cùng không làm gì cả. Ngoài ra, có một phương pháp được gọi là .clear()nên được ưa chuộng. –  07:38:46 26/10/2016
  • 1
    Tại sao bạn lại sử dụng vòng lặp for () thay vì vòng lặp while ()? Những gì bạn đang làm chẳng có ý nghĩa cho Looping –  13:26:29 07/12/2016
  • 1
    tôi cho rằng Sackurise muốn viết i-=1;để giải thích cho phần tử hiện đang bị thiếu. Nhưng nó tốt hơn để phục hồi các vòng lặp: for(int i=sparseArray.size()-1; i>=0; i++){...; hoặcwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);... –  13:08:18 17/01/2017
  • 1
    Các tham chiếu như "vòng lặp ở trên" không có ý nghĩa gì cả. –  08:48:47 23/02/2018
  • 1
    Tôi nghĩ rằng điểm của một 'trình lặp' là loại bỏ đối tượng an toàn. Tôi chưa thấy bất kỳ ví dụ nào về lớp Iterator với các lớp SliceArrays giống như có cho các bản đồ băm. Điều này gần nhất với việc giải quyết loại bỏ đối tượng an toàn, tôi hy vọng nó hoạt động mà không có ngoại lệ sửa đổi đồng thời. –  15:49:50 31/03/2018
5

Đây là cách triển khai Iterator<T>và đơn giản Iterable<T>cho 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);
    }
}

Nếu bạn muốn lặp lại không chỉ một giá trị mà còn cả một khóa:

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

Thật hữu ích khi tạo các phương thức tiện ích trả về 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);
    }
}

Bây giờ bạn có thể lặp lại SparseArray<T>:

SparseArray<String> a = ...;

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

for (SparseKeyValue<String> s: SparseArrayUtils.keyValueIterable(a)) {
  // ...
}
|
4

Nếu bạn sử dụng Kotlin, bạn có thể sử dụng các chức năng mở rộng như vậy, ví dụ:

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

Bạn cũng có thể chuyển đổi thành một danh sách, nếu bạn muốn. Thí dụ:

sparseArray.keysIterator().asSequence().toList()

Tôi nghĩ rằng nó thậm chí có thể được an toàn đến các mục delete sử dụng removetrên LongSparseArraybản thân (không phải trên iterator), vì nó là thứ tự tăng dần.


CHỈNH SỬA: Có vẻ như thậm chí còn có một cách dễ dàng hơn, bằng cách sử dụng collection-ktx (ví dụ ở đây ). Nó được thực hiện theo một cách rất giống với những gì tôi đã viết, trên thực tế.

Gradle yêu cầu điều này:

implementation 'androidx.core:core-ktx:#'
implementation 'androidx.collection:collection-ktx:#'

Đây là cách sử dụng cho LongSparseArray:

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

Và đối với những người sử dụng Java, bạn có thể sử dụng LongSparseArrayKt.keyIterator, LongSparseArrayKt.valueIteratorLongSparseArrayKt.forEachchẳng hạn. Tương tự đối với các trường hợp khác.

|
-5

Câu trả lời là không vì SparseArraykhông cung cấp nó. Như pstđã nói, thứ này không cung cấp bất kỳ giao diện nào.

Bạn có thể lặp lại 0 - size()và bỏ qua các giá trị trả về null, nhưng đó là về nó.

Như tôi đã nêu trong nhận xét của mình, nếu bạn cần lặp lại, hãy sử dụng a Mapthay vì a SparseArray. Ví dụ: sử dụng một TreeMaplặp lại theo thứ tự của khóa.

TreeMap<Integer, MyType>
|
-6

Câu trả lời được chấp nhận có một số lỗ hổng trong đó. Vẻ đẹp của SparseArray là nó cho phép các khoảng trống trong các miếng. Vì vậy, chúng ta có thể có hai bản đồ như vậy, trong SparseArray ...

(0,true)
(250,true)

Lưu ý rằng kích thước ở đây sẽ là 2. Nếu chúng ta lặp lại quá kích thước, chúng ta sẽ chỉ nhận được giá trị cho các giá trị được ánh xạ tới chỉ mục 0 và chỉ mục 1. Vì vậy, ánh xạ với khóa là 250 không được truy cập.

for(int i = 0; i < sparseArray.size(); i++) {
   int key = sparseArray.keyAt(i);
   // get the object by the key.
   Object obj = sparseArray.get(key);
}

Cách tốt nhất để làm điều này là lặp lại kích thước của tập dữ liệu của bạn, sau đó kiểm tra các phần đó bằng get () trên mảng. Đây là một ví dụ với bộ điều hợp mà tôi cho phép xóa hàng loạt các mục.

for (int index = 0; index < mAdapter.getItemCount(); index++) {
     if (toDelete.get(index) == true) {
        long idOfItemToDelete = (allItems.get(index).getId());
        mDbManager.markItemForDeletion(idOfItemToDelete);
        }
    }

Tôi nghĩ lý tưởng nhất là họ SparseArray sẽ có một phương thức getKeys (), nhưng than ôi thì không.

|
  • 1
    Bạn đã nhầm - keyAtphương thức trả về giá trị của khóa thứ n (trong ví dụ của bạn keyAt(1)sẽ trả về 250), không nên nhầm lẫn với getnó trả về giá trị của phần tử được tham chiếu bởi khóa. –  20:34:31 19/06/2015
  • 1
    Tôi không chắc 'cái này' trong nhận xét của bạn là gì. Bạn đang thừa nhận rằng câu trả lời của bạn là sai, hay bạn đang nói rằng nhận xét của tôi là sai? Nếu sau này, vui lòng kiểm tra developer.android.com/reference/android/util/… –  10:00:45 29/01/2016
  • 1
    Câu trả lời của tôi sai, tôi sẽ không xóa đi để người khác học hỏi. –  16:58:40 29/01/2016

Câu trả lời của bạn (> 20 ký tự)

Bằng cách click "Đăng trả lời", bạn đồng ý với Điều khoản dịch vụ, Chính sách bảo mật and Chính sách cookie của chúng tôi.

Không tìm thấy câu trả lời bạn tìm kiếm? Duyệt qua các câu hỏi được gắn thẻ hoặc hỏi câu hỏi của bạn.