2

Can you see why this returns true:

Map<IObject, XYZ> records1Map = getRecords(1); // returns Map<MyObject, XYZ>
Map<IObject, XYZ> records2Map = getRecords(2); // returns Map<MyObject, XYZ>
for (IObject rec : records1Map.keySet())
{
    System.debug(records2Map);
    if (!records2Map.containsKey(rec)) return false;
}
return true;

but this returns false (same as above but without the debug statement)?

for (IObject rec : records1Map.keySet())
{
    if (!records2Map.containsKey(rec)) return false;
}
return true;

These are the method overrides:

public class MyObject implements IObject
{
    public SObject record { get; set; }

    public Boolean equals(Object o)
    {
        if (o instanceof MyObject)
        {
            MyObject compareTo = (MyObject)o;
            return this.record == compareTo.record;
        }
        else return false;
    }

    public Integer hashCode()
    {
        return JSON.serialize(record).hashCode();
    }
}

Btw I am aware of this question, however my overridden method signatures are correct.

6
  • 1
    Be aware that debugging settings affect whether hashCode is called - see hashCode() is never called when adding to Maps and Sets. And there have been bugs in the past such as System.hashCode broken on Id properties of SObjects?.
    – Keith C
    May 19 '16 at 19:32
  • Is this only an issue in unit tests? Because in my case I was actually running one! So would I be "fixing" the issue if I added the debug statement?
    – Mossi
    May 19 '16 at 19:50
  • Minor quibble, but you don't override anything when you implement these methods.
    – Adrian Larson
    May 19 '16 at 19:57
  • Think the core issue was the debug level not the System.debug statement but who knows how the two might or might not interact.
    – Keith C
    May 19 '16 at 19:57
  • 1
    By the way the System.hashCode method is advertised as working on SObjects.
    – Keith C
    May 19 '16 at 20:43
2

I cannot explain your results, though see the comment I've added to the question.

My attempts at trying to use value equality between SObject instances (where all the fields are compared) have never gone well. Such comparisons also use up a lot of the governor limited CPU time if many fields are involved. In the short term you may be fine, but if you use this approach everywhere in your code, eventually you will hit a case where hundreds of SObjects are being compared and the CPU time governor limit will be exceeded.

I recommend that you primarily stick to the simple strategy of comparing the ID values only: you could do that in a wrapper class like your MyObject if you wanted to. Then only use value equality for situations where that logic is specifically required.

3
  • Serialization is also a huge time hog.
    – Adrian Larson
    May 19 '16 at 20:10
  • That was a narrowed down sample code. I actually compare the Ids if they are present. But how do you recommend I compare the wrappers in absence of an id?
    – Mossi
    May 19 '16 at 20:18
  • @Mossi OK that's good. You could add an Integer "in memory only" ID to the wrapper populated from an incrementing static and use that if no ID is present (assuming you treat the wrapper as immutable). Or just don't wrap SObjects that don't have ID values as usually they are created/handled by explicit code that can keep track of them. But yeah maybe the value equality is the way to go for that case.
    – Keith C
    May 19 '16 at 20:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.