10

Just wasted some time trying to get a big business logic unit test to pass and tracked it down to this.

Here is a failing test demonstrating just the problem of using System.hashCode for Id values:

@isTest
private class HashCodeTest {

    @isTest
    static void test() {

        Account a = new Account(Name = 'Acme');
        insert a;
        Id aId = a.Id;

        Set<Integer> s1 = new Set<Integer>();
        Set<Integer> s2 = new Set<Integer>();
        Set<Integer> s3 = new Set<Integer>();

        Integer n = 8;
        for (Integer i = 0; i < n; i++) {
            s1.add(System.hashCode(String.valueOf(a.Id)));
            s2.add(System.hashCode(aId));
            s3.add(System.hashCode(a.Id));
            System.assert(a.Id == a.Id);
        }

        System.assertEquals(1, s1.size());
        System.assertEquals(1, s2.size());

        // This fails: same (equals) object but n different hashCodes not 1
        System.assertEquals(1, s3.size());
    }
}

The last assertion demonstrates the problem: referencing the Id field of an object returns an Id object that has a different hashCode every time (n in the set instead of 1 in the set) even though the Id values are equal. So the normal equals/hashCode contract is violated.

Please comment/answer if I'm missing something here - to me this looks like an Apex bug but I may be deluding myself.

PS

Probably related to but not the same as Id object and String object representing the same entity ID are equal but don't have the same .hashCode().

6
  • You should probably get a PM to look into this.
    – sfdcfox
    Jul 9, 2015 at 21:36
  • For what it is worth, I ran the body of the test method as anonymous apex against API v34.0. It completed with no assertion issues. This was against na5, which is currently reporting the release as Summer '15 Patch 13.1. Jul 9, 2015 at 23:44
  • @DanielBallinger Meaning it is broken in that patch too right?
    – Keith C
    Jul 9, 2015 at 23:48
  • @KeithC Maybe I misinterpreted your assertions. Are you expecting the last assertion to fail or pass to indicate a problem with the hashcode method? You're saying that every time you call System.hashCode(a.Id) you are getting a different integer back? Jul 9, 2015 at 23:57
  • @DanielBallinger I've reversed the pass/fail so it would pass if hashCode behaved as I expect (1 value in the set) but presently fails (8 values in the set). So if you run the code as posted now it will fail.
    – Keith C
    Jul 10, 2015 at 0:06

2 Answers 2

9

The workaround is shown in the question: convert the Id to a String first to get a consistent hashCode:

Account a = ...;
Integer hashCode = System.hashCode(String.valueOf(a.Id))

PS I've created a case with Salesforce reporting the bug.

1
  • Just got this from Salesforce support: R&D confirmed this issue will be resolved along with the known issue: "all hashcodes should be the same provided the criteria is met". I tried to find that known issue but could't.
    – Keith C
    Aug 7, 2015 at 22:28
3

Update: Fixed!

I just tested this in a Spring '21 sandbox and it is fixed! The Known Issue still says In Review but I assume that's because the release isn't in production instances yet.

Note that this is a versioned behavior change beginning with version 51.0. It's called out in the release notes here.


This is still broken. I worked with support to get the scenario added to the known issue referenced in the original question (https://success.salesforce.com/issues_view?id=a1p300000008YQ0AAM).

Note that I reproduced the issue a bit differently, using the following code:

Id id1 = UserInfo.getUserId();
Id id2 = UserInfo.getUserId();
// passes, the ids are equal
System.assert(id1.equals(id2));
// fails, the hashcodes are not equal
System.assertEquals(System.hashCode(id1), System.hashCode(id2));

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .