Làm thế nào để lặp lại thông qua SpzzyArray?


311

Có cách nào để lặp lại trên Java SpzzyArray (cho Android) không? Tôi đã sử dụng sparsearrayđể dễ 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
Ồ, nói về một lớp hoàn toàn không được yêu thích, phù hợp với giao diện bộ sưu tập ZERO ...

1
Bạn có thể sử dụng một TreeMap<Integer, MyType>cái sẽ cho phép bạn lặp theo thứ tự theo khóa. Như đã nêu, SpzzyArray được thiết kế để hiệu quả hơn HashMap, nhưng nó không cho phép lặp lại.
John B

2
rất, rất khó có khả năng hiệu suất của bản đồ mà bạn chọn sẽ là nút cổ chai trong ứng dụng của bạn.
Jeffrey Blattman

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 phù hợp.
sương giá

1
@frostymarvelous nói rằng TWICE nhanh như vậy, điều đó có thể có nghĩa là tiết kiệm dưới 10ms. 10ms có liên quan trong sơ đồ lớn hơn của ứng dụng không? Có đáng để sử dụng một giao diện phụ tối ưu khó hiểu và duy trì hơn 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à "hoàn toàn sử dụng mảng thưa thớt".
Jeffrey Blattman

Câu trả lời:


537

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

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

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
tài liệu nói rằng "keyAt (int index) Đưa ra một chỉ mục trong phạm vi 0 ... size () - 1, trả về khóa từ ánh xạ giá trị khóa-chỉ mục mà SpzzyArray 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 được mô tả bởi bạn.
Ruzanna

12
tốt hơn là 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.
Dmitry Zaytsev

25
Ở đây có dễ sử dụng hàm valueAt hơn không?
Milan Krstic

34
Điều này cũng sẽ hoạt động trong vòng lặp:Object obj = sparseArray.valueAt(i);
Florian

27
valueAt(i)nhanh hơn get(key), bởi vì valueAt(i)keyAt(i)đề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.
Mecki

180

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

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

7
Sử dụng valueAt () rất hữu ích (và nhanh hơn giải pháp được chấp nhận) nếu phép lặp của bạn không quan tâm đến các khóa, nghĩa là: một vòng lặp đếm xuất hiện của một giá trị cụ thể.
Sogger

2
Lấy sparseArray.size()một biến để nó không gọi size()mỗi lần.
Pratik Butani

4
Nó là dự phòng để sao chép kích thước () 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 làm như vậy trước khi bạn đề xuất những điều như vậy ... Tôi nhớ một thời gian cách đây 20 năm, nơi chúng tôi có các danh sách liên kết đơn giản thực sự phải đếm kích thước của chúng mỗi khi bạn hỏi chúng nhưng tôi không tin rằng những thứ như vậy vẫn còn tồn tại ...
1

Điều này có được đảm bảo theo thứ tự quan trọng không?
HughHughTeotl

18

Ooor bạn chỉ cần tạo ListIterator của riêng bạn:

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 có vẻ hơi quá kỹ thuật. Thật tuyệt vời tho
hrules6872

12

Đơn giản như Pie. Chỉ cần chắc chắn rằng bạn lấy 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.


11

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

Chỉ cần gọi forEach { i, item -> }


vâng, bạn thực sự đúng. xấu 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ờ có một suy nghĩ thứ hai rằng câu trả lời này là một tài liệu tham khảo 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 có thể vui lòng chỉnh sửa câu trả lời của mình và thêm android-ktx tôi sẽ nâng cấp nó)
Yazazzello

@Yazazzello hey tôi thậm chí còn không biết về Android KTX, điểm tốt!
0101100101

7

Để loại bỏ tất cả các yếu tố khỏi SparseArrayviệc sử dụng các vòng lặp ở trên dẫn đếnException .

Để tránh điều này Thực hiện theo mã dưới đây để loại bỏ tất cả các yếu tố khỏi SparseArrayviệc sử dụng cá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;
    }
}

2
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.
Paul Woitaschek

Tại sao bạn sẽ sử dụng vòng lặp for () thay vì while ()? Những gì bạn đang làm không có ý nghĩa cho việc lặp lại
Phil A

tôi giả sử Sackurise muốn viết i-=1;vào tài khoản cho phần tử hiện đang thiếu. Nhưng tốt hơn là hoàn nguyên vòng lặp : for(int i=sparseArray.size()-1; i>=0; i++){...; hoặcwhile (sparseArray.size()>0) { int key=sparseArray.keyAt(0);...
THS

Các tham chiếu như "vòng lặp ở trên" không có ý nghĩa gì cả.
Không thể tin được vào

Tôi nghĩ rằng điểm của 'iterator' 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 thưa thớt như có cho hashmap. Điều này đến gần nhất để 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.
Androidcoder

5

Đây là đơn giản Iterator<T>Iterable<T>thực hiện 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 là 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.


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

Lớp yêu cầ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.forEach, ví dụ. Tương tự cho 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, điều này không cung cấp bất kỳ giao diện nào.

Bạn có thể lặp từ 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 Mapthay vì a SparseArray. Ví dụ, sử dụng một TreeMapcái lặp theo thứ tự theo 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 SpzzyArray là nó cho phép các khoảng trống trong phân. Vì vậy, chúng ta có thể có hai bản đồ như vậy, trong một SpzzyArray ...

(0,true)
(250,true)

Lưu ý kích thước ở đây sẽ là 2. Nếu chúng tôi lặp lại theo kích thước, chúng tôi sẽ chỉ nhận các giá trị cho các giá trị được ánh xạ tới chỉ số 0 và chỉ mục 1. Vì vậy, ánh xạ với khóa 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 số đó bằng get () trên mảng. Dưới đây là một ví dụ với một bộ chuyển đổi nơi 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 là gia đình SpzzyArray sẽ có phương thức getKeys (), nhưng than ôi thì không.


4
Bạn đã sai - 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 bị nhầm lẫn với getgiá trị nào trả về giá trị của phần tử được tham chiếu bởi khóa.
Eborbob

Tôi không chắc 'cái này' trong bình luận của bạn là gì. Bạn có thừa nhận rằng câu trả lời của bạn là sai, hoặc 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/
Kẻ

17
Câu trả lời của tôi là sai, tôi sẽ không xóa nó để người khác có thể học.
Tyler Pfaff
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.