0

Today I have written a simple class to check the behaviour of hashCode in the sfdc.

Related to documentation salesforce:

"Uniqueness of set elements of user-defined types is determined by the equals and hashCode methods, which you provide in your classes. Uniqueness of all other non-primitive types is determined by comparing the objects’ fields."

"Note the following limitations on sets: Unlike Java, Apex developers do not need to reference the algorithm that is used to implement a set in their declarations (for example, HashSet or TreeSet). Apex uses a hash structure for all sets."

"The hashcode method in Apex is based on the hashcode method in Java."

I have executed the same code in Java and the result shows 2 x Test

Link to Set documentation Link hashCode documentation

If you have any idea give advice or suggestion.

In my perspective when we are adding values to Set, method hashCode is not executing just equals method.

public class UserT {

    public String name;
    public String email;

    public Boolean equals(Object o) {
        if (o instanceof UserT) {
            UserT u = (UserT)o;
            return name.equals(u.name);
        }
        return false;
    }

    public Integer hashCode() {
        return 31 * name.hashCode() * 5 * email.hashCode();
    }
}

Execution

Map<UserT, String> users = new Map<UserT, String>();
        UserT  u1 = new UserT();
        UserT  u2 = new UserT();
        u1.name = 'Test';
        u1.email = 'a@test.com';
        u2.name = 'Test';
        u2.email = 'b@test.com';
        users.put(u1, 'test');
        users.put(u2, 'test');
        for (UserT u: users.keySet()) {
            System.debug(u.name);
        }   
System.debug('Hash code u1 ' + u1.hashCode());
System.debug('Hash code u2 ' + u2.hashCode());
System.debug(u1.equals(u2));

Result:

enter image description here

After execution my code(Apex code - Debug) result has been expected shows 2x Test. Why in sfdc we have different logic executed based off debug mode(Debug and FINEST).

Java code:

enter image description here


@sfdcfox

I have read your answer What's in a Hash Collision?

Also I have executed your code on my sandbox. Result is diffrent hashCode called: 0

That code

public class KeyTrial {
    public static Integer eCounter = 0, hCounter = 0;
    Integer value;
    public KeyTrial(Integer val) {
        value = val;
    }
    public Boolean equals(Object o) {
        eCounter++;
        return ((KeyTrial)o).value == value;
    }
    public Integer hashCode() {
        hCounter++;
        return value;
    }
}
Set<KeyTrial> trials = new Set<KeyTrial>();
Long startTime = DateTime.now().getTime();
for(Integer i = 0; i < 100; i++) {
    trials.add(new KeyTrial((Math.random()*10000000).intValue()));
}
Long endTime = DateTime.now().getTime();
System.debug(LoggingLevel.error, 'equals called: '+KeyTrial.eCounter);
System.debug(LoggingLevel.error, 'hashCode called: '+KeyTrial.hCounter);
System.debug(LoggingLevel.error, 'time elapsed: '+(endTime-startTime));

enter image description here

Solution:

After execution, my code(Apex code - Debug) result has been expected to show 2x Test. Why in SFDC, we have different logic executed based on debug mode(Debug and FINEST).

enter image description here

enter image description here

2

You need the equals and hashCode implementations to be compatible, and in your implementation they are not. Both play a part in how a Map is managed. First the hashCode is used to identify the "bucket" into which an object will be placed, while equals is used to determine whether an object matches one already in the "bucket".

Because your equality ignores the email address, you get spurious matches in the Map's bucket.

Either do as @sfdcfox suggests and remove email from the hashCode or add email to equals.

BTW, I mention Map, rather than Set, because I suspect these are actually sharing implementation underneath the covers... but that's just a guess.

2
  • I have added system.debug('test hashcode') to the hashcode method and is not showing on. Could you tell why? Looks hashcode method is no execute by apex.
    – Kowalek
    Jul 31 at 4:26
  • After change code debug mode to debug instead of finest. It works fine
    – Kowalek
    Aug 4 at 12:04
2

You're factoring email into the hash code, which is different for each user. Therefore, you end up with set that has two items. hashCode() is called first, then equals is called afterwards if there's a hash collision.

You can fix this by changing the hashCode to:

public Integer hashCode() {
    return name.hashCode();
}

EDIT Hash codes are different (no hash collision). Equals was called as first, because check only name.

7
  • I think hash collision is when we have two objects in the same hashcode for that case we have 2 different hashcodes. More details about hash collision see point 6 -> baeldung.com/java-hashmap-advanced I added java code with execution.
    – Kowalek
    Jul 30 at 19:56
  • @Kowalek No, if you have a hash code collision, they have the same code but are not equal. In the OP code, we were not getting an expected collision, so there were two distinct values.
    – sfdcfox
    Jul 31 at 0:16
  • @Kowalek I saw you edit; equals will only be called when a hash code collision occurs. It will not be called if the hash code does not match an existing object. See also What's in a Hash Collision?
    – sfdcfox
    Jul 31 at 0:18
  • I have read you respone in "What's in a Hash Collision?"
    – Kowalek
    Jul 31 at 5:11
  • @Kowalek The edit implies that equals was called, but it would not be in a scenario where there is no hash collision.
    – sfdcfox
    Jul 31 at 5:22

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.