4

I am using a SparseIntArray and I am puzzled by this behavior:

public static SparseIntArray getArray()
{
    SparseIntArray result = new SparseIntArray();
    result.append(0, 99);
    result.append(1, 988);
    result.append(2, 636);

    return result;
}

public static void testArray()
{
    SparseIntArray first = getArray();
    SparseIntArray second = getArray();
    if( first.equals(second) )
    {
        Log.v(TAG,"first "+first.toString()+" == second "+second.toString());           
    }
    else
    {
        Log.v(TAG,"first "+first.toString()+" != second "+second.toString());
    }
}

Output:

11-06 14:53:15.011: V/fileName(6709): first {0=99, 1=988, 2=636} != second {0=99, 1=988, 2=636}

I know that using == between two objects will compare the object addresses, which in this case are different, but here I am using SparseIntArray.equals(Object other) and the intended result is not unexpected.

I am sure I can roll my own compare method, but it sounds kind of silly. What is the point of having a base class Object.equals(Object other) method if we cant rely on it?

Can someone point to any mistake?

Eran
  • 387,369
  • 54
  • 702
  • 768
Bamaco
  • 592
  • 9
  • 25
  • Looks like an implementation choice from Android devs... It is really to them that you should ask the question. But if you need equals and hashcode, why don't you use a regular map instead? – fge Nov 06 '14 at 20:12
  • @fge, I started using the regular map (excuse my C++ like syntax), then the IDE warned my that SparseIntArray was more efficient. Regular maps can use Integer, which require some kind of boxing, while SparseIntArray is limited to keys and values of int types, it has less overhead. – Bamaco Nov 06 '14 at 20:39
  • OK, but do you have a need for this (memory) efficiency? Ultimately, that's a tradeoff between this and practicality (you have to code a method for equality) – fge Nov 06 '14 at 20:41

2 Answers2

3

I just searched for the code of SparseIntArray. If you are referring to android.util.SparseIntArray, it doesn't override equals, which means it uses the default implementation of Object class, which compares the references.

What is the point for having a base class Object.equals(Object other) method if we cant rely on it?

Actually, you can't rely of the base class Object.equals, since it does precisely what you don't want to do:

public boolean equals(Object obj) 
{
    return (this == obj);
}

It's up to the writers of any class to decide whether to override equals and give a different implementation.

Eran
  • 387,369
  • 54
  • 702
  • 768
  • 1
    The implementtation of SparseIntArray https://code.google.com/p/smali/source/browse/dexlib/src/main/java/org/jf/dexlib/Util/SparseIntArray.java?r=5934004fe3c1e9617793aa120e88f5df1b651c14 – Salih Erikci Nov 06 '14 at 20:12
1

@Eran is right, Object.equals(Object) does not cut it.

I have made a simple static method to compare two instances

public static boolean compareSame( SparseIntArray first, SparseIntArray second )
{
    // compare null
    if( first == null )
    {
        return (second == null);
    }
    if( second == null )
    {
        return false;
    }

    // compare count
    int count = first.size();
    if( second.size() != count )
    {
        return false;
    }

    // for each pair
    for( int index = 0; index < count; ++index )
    {
        // compare key
        int key = first.keyAt(index);
        if( key != second.keyAt(index))
        {
            return false;
        }

        // compare value
        int value = first.valueAt(index);
        if( second.valueAt(index) != value)
        {
            return false;
        }
    }       

    return true;
}

I will probably end up deriving my own version of SparseIntArray and override the equals method, I think this is more clean.

[EDIT] Here is the code for an sub-class implementing equals

import android.util.SparseIntArray;

public class SparseIntArrayComparable extends SparseIntArray {
@Override
public boolean equals( Object obj ) {

    if( obj instanceof SparseIntArray ) {
        SparseIntArray other = (SparseIntArray)obj;

        // compare count
        int count = size();
        if( count != other.size() )
            return false;

        // for each pair
        for( int index = 0; index < count; ++index ) {

            if( keyAt(index) != other.keyAt(index))
                return false;

            if( valueAt(index) != other.valueAt(index) )
                return false;
        }       

        return true;
    }
    else
        return false;
    }
}
Bamaco
  • 592
  • 9
  • 25
  • You probably shouldn't care about keys and values being stored at different position in the internal array. Just that both sparse arrays contain the same keys pointing to the same values. I.e. the size is the same and for each key-value pair in the first array there's the same value under the same key in the second array. – Eugen Pechanec Nov 05 '19 at 13:01