Take the 2-minute tour ×
Salesforce Stack Exchange is a question and answer site for Salesforce administrators, implementation experts, developers and anybody in-between. It's 100% free, no registration required.

Being able to write your own equals/hashCode for a custom class seems appealing - see Non-primitive Types in Map Keys and Sets. But with no hashCode method exposed in Apex string or decimal or any of the other primitive types, building a correct and efficient hashCode for a custom class that has a few fields of different types looks way harder than it should be.

Suggestions?

share|improve this question
1  
Yes most interesting, wondering why the only sample is using numbers as an example? ;-) Looks like there is a bug in this area at large anyway, success.salesforce.com/issues_view?id=a1p30000000SV0XAAW –  Andrew Fawcett Nov 19 '12 at 23:17
    
Good gawd, that bug makes the entire feature unusable if it's really that simple a repro. Even if it wasn't there, Keith is right, not exposing hashCode in the primitives makes it more painful to roll your own. Until that happens, best bet is to create a set of primitive hash utility methods that copy the logic from Java or .NET. Still the point is moot until they fix maps and sets. –  jkraybill Nov 20 '12 at 1:11
    
On na14 these pass: –  Keith C Nov 20 '12 at 9:33
    
I saw the bug report but couldn't reproduce. What I see is that these pass on na14 but not on na3 system.assertEquals(1, numberSet.size()) system.assertEquals(1, numberMap.size()) and this fails on both system.assertEquals(t1, t2). A new low has been reached here. –  Keith C Nov 20 '12 at 9:43

2 Answers 2

up vote 7 down vote accepted

Summer'13 Update:

According to the Summer'13 release notes String now provides a hashCode method! Here is the sample code included in the release notes here.

public class MyCustomClass {
    String x,y;
    public MyCustomClass(String a, String b) {
        x=a;
        y=b;
    }
    public Integer hashCode() {
        return (31 * x.hashCode()) ^ y.hashCode();
    }
    public Boolean equals(Object obj) {
        if (obj instanceof MyCustomClass) {
            MyCustomClass p = (MyCustomClass)obj;
            return (x.equals(p.x)) && (y.equals(p.y));
        }
        return false;
    }
}

Original Answer:

Converted comment to an answer after a bit of digging around.

Initial thoughts...

Yes most interesting, I did wonder why the only sample was using numbers. It also looks like there is a bug in this area at large anyway, http://success.salesforce.com/issues_view?id=a1p30000000SV0XAAW.

Current conclusion...

I had a look at the Java implementations and a few other general postings on the net. My conclusion is that given the statement governor, at least for strings, it is going to quite expensive to implement a String.hashCode. We really need a native implementation of this to avoid hitting the statement governor very quickly with large maps.

Some interesting links

share|improve this answer
    
Could an MD5 hash generated using the Crypto class be used as a substitute? –  techtrekker Nov 20 '12 at 10:06
    
Have just been trawling the Apex docs and came across that, the trick is going to be turning this into an Integer to return from the hashCode method. –  Andrew Fawcett Nov 20 '12 at 10:14
    
It shouldnt matter right, if the string (blobs) are 'equal', the resulting hashes should be the same ? –  techtrekker Nov 20 '12 at 11:06
    
I'm just not sure how both equals and hashCode could be implemented this way though? (putting aside its a big hammer for a large nut of course) How would the hashCode method be implemented? –  Andrew Fawcett Nov 20 '12 at 11:15
2  
Posted Expose hashCode on all Apex primitives so hashCode viable for custom classes in the IdeaExchange to at least publicise that particular problem a bit more. –  Keith C Nov 21 '12 at 18:28

I realize Summer '13 is just around the corner, but if you are still looking for a way to create a hashcode out of strings (or any other object) without hitting governor limits, here is a method I pieced together using the solution found here

private static final Long prime = 524287L;
public static Integer getHashCode(Object obj) {
    String objHex = null;
    if(obj instanceof Id) {
        objHex = EncodingUtil.convertToHex(Blob.valueOf((String)obj));
    } else if(obj != null) {
        Blob objJSONBlob = Blob.valueOf(JSON.serialize(obj));
        Blob objHMAC = Crypto.generateMac('hmacSHA1', objJSONBlob, Blob.valueOf('a key that does not matter'));
        objHex = EncodingUtil.convertToHex(objHMAC);
    }
    if(objHex != null) {
        Long hash = 0L;
        for(String sChar : objHex.split('')){
            if(String.isEmpty(sChar)) {
                continue;
            }
            hash = ((hash ^ hexToInteger.get(sChar)) * prime);
        }
        return (Integer)hash;
    } else {
        return 0;
    }
}
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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