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