Search code examples
javaandroidandroid-sparsearray

Android SparseArray value comparison


So I was writing some very simple code and found a very unexpected behavior. All List and Map implementations use the equals method of a node to compare them. So if you would've a list of strings, and you would try to acquire the index of a string in the list, you don't need to use the same object. Example:

List<String> list = new ArrayList<>();
list.add("test");
int index = list.indexOf("test");
System.out.println(index);//returns 0

I've noticed that all the SparseArray classes of Android use == instead of equals to compare nodes. Example method (LongSparseArray.java):

public int indexOfValue(E value) {
    if (mGarbage) {
    gc();
    }

for (int i = 0; i < mSize; i++) {
    if (mValues[i] == value) {
        return i;
        }
    }
        return -1;
}

So if you have simple code like this:

LongSparseArray<String> localContacts = new LongSparseArray<>();
localContacts.put(2, "test");
int index = localContacts.indexOfValue("test");
System.out.println(index);

The index here would return -1 (which is pretty unexpected if you don't how the value is compared).

So I was wondering... why does Android not use equals? This is way more handy and preferred (from a Java point of view). Now I have to loop through all the values of a SparseArray and compare them my self, which results in more (unneeded) code (or use a Map which causes less performance in Android).


Solution

  • Looking at the source code for LongSparseArray, it appears this method does exist - but that it's hidden (for some reason):

    /**
    * Returns an index for which {@link #valueAt} would return the
    * specified key, or a negative number if no keys map to the
    * specified value.
    * <p>Beware that this is a linear search, unlike lookups by key,
    * and that multiple keys can map to the same value and this will
    * find only one of them.
    * <p>Note also that this method uses {@code equals} unlike {@code indexOfValue}.
    * @hide
    */
    public int indexOfValueByValue(E value) {
        if (mGarbage) {
            gc();
        }
    
        for (int i = 0; i < mSize; i++) {
            if (value == null) {
                if (mValues[i] == null) {
                    return i;
                }
            } else {
                if (value.equals(mValues[i])) {
                    return i;
                }
            }
        }
        return -1;
    }
    

    You can see that all this code is really doing is what you said in your question - looping through all values until finds the correct one, and returning its index.

    I don't know why this has been excluded from the public API, but it's one more point against using Sparse*** anything in my opinion. They're often too basic to be useful for my requirements.