1

I have a wrapper class as below:

    public class WrapperShoppingCart{
    public opportunityLineItem oppLineitem {get;set;}
    public Boolean selected {get;set;}
    public Id idValue {get;set;}

    public WrapperShoppingCart(opportunityLineItem oLineItem ){
        oppLineitem = oLineItem;
        selected = false;
    }

    public Boolean equals(Object obj){
        if(obj!= null && obj instanceOf OpportunityLineItem){
            OpportunityLineItem li = (OpportunityLineItem) obj;
            if((li.id == oppLineitem.id)){
                return true;
            }else{
                return false;
            }
        }else{
            return false;
        }
    }

    public Integer hashCode() {
        return Crypto.getRandomInteger();
    }       

}

And when I run the below code:

    Set<WrapperShoppingCart> mySet = new <WrapperShoppingCart>();
    //Here there is some code to intialize the collection.
    mySet.remove(alreadyExistingOppLineItemInSet);

Even after calling the remove method on the set collection by passing one of the existing item in the set, set collection still has the object in it, Could somebody please tell me if this is because of the way I implemented the equals and hashCode method?

1
  • 4
    return Crypto.getRandomInteger(); Alarm bells are going off in me head for this. I'm pretty sure you want to consistently return the same value each time for the same instance. Jun 18 '15 at 21:14
4

A way to think of a hashCode as used in maps and sets is that it is used to divide up all possible values into ranges that determine which "bucket" is used to hold a list of the objects. (A Java map starts out with 16 buckets.) So when a map is being checked for whether it contains an object, first the bucket is identified by using the hashCode then the equals method is used to see if any of the objects in the bucket match. Being able to jump to the right bucket and just check a couple of objects is how maps improve performance compared to code having to run through every element of a long list.

So if the hashCode varies from call to call, the wrong bucket will be looked in and the object won't be found - a nasty bug.

The above is the reason for the spec than Daniel quotes.

You can often avoid implementing equals and hashCode when wanting to just keep one instance (and an ID is present) by putting your values in a map keyed by the ID. ID already (in theory at least) has a correct implementation of equals and hashCode

PS

In response to your comment, the question is which attributes of opportunityLineItem represent duplication that you want to filter. Combine those in your equals and hashCode logic rather than using the missing ID.

But your use of a set might not work. Once an object is put in a set, if you then change its values (though say a Visualforce page) the set logic will be broken. The set duplication logic (that invokes equals and hashCode) runs when you add an object: it isn't automatically re-run when you change an object.

If your aim is to eliminate duplicates, stick to a list and run some duplicate checking logic when the user saves (perhaps using a temporary set then when the object are no longer changing). That way you can explicitly report the problem and get them to make the right fix rather than implicitly discarding some data via your set.

2
  • Keith, The problem is the objects in the set collection are not yet saved to database so I dont have the id of the oppLineItem to go with the ID. Can you give me some idea on how to generate hashcode if ID is not yet available?
    – javanoob
    Jun 18 '15 at 21:55
  • @javanoob See the PS.
    – Keith C
    Jun 18 '15 at 22:25
4

hashCode must return the same value each time. Try:

public Integer hashCode() {
    return oppLineItem == null || oppLineItem.Id == null? 0 :
           System.hashCode(oppLineItem.Id);
}

Also, your equals function is verbose:

public Boolean equals(Object other) {
    return ((WrapperShoppingCart)other).oppLineItem.Id == oppLineItem.Id;
}

Again, if you can't verify nulls, then you need to check for nulls all the way through. Finally... your equals function is seeing if other is an OpportunityLineItem. It's not, it's a WrapperShoppingCart.

2

From Adding equals and hashCode Methods to Your Class

Keep in mind the following when implementing the hashCode method.

  • If the hashCode method is invoked on the same object more than once during execution of an Apex request, it must return the same value.
  • If two objects are equal, based on the equals method, hashCode must return the same value.
  • If two objects are unequal, based on the result of the equals method, it is not required that hashCode return distinct values.

I've emphasised the first point here, but they are all applicable.

Your implementation won't meet any of those criteria:

public Integer hashCode() {
    return Crypto.getRandomInteger();
}     

Instead, consider an implementation based on the known members.

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.