1

The documentation (not in the class docs, though) says this

If set elements are objects, and these objects change after being added to the collection, they won’t be found anymore when using, for example, the contains or containsAll methods, because of changed field values. https://developer.salesforce.com/docs/atlas.en-us.apexcode.meta/apexcode/apex_set_sobject.htm

I think it is a poor excuse, but anyway.

Set<TestObject__c> s = new Set<TestObject__c>();
TestObject__c t1 = new TestObject__c(Name='000111',AccEmail__c='email@site.com',Message__c='OLD');
TestObject__c t2 = new TestObject__c(Name='000111',AccEmail__c='email@site.com',Message__c='NEW');
system.debug(s.add(t1)); // Logs true
// Make t1 the same as t2
t1.Message__c = 'NEW';
// Add t2. Should return false and not get added.
system.debug(s.add(t2)); // Logs true
system.debug(s.size());  
system.debug(s); 
system.debug(new List<TestObject__c>(s)); 
system.debug(new List<TestObject__c>(new Set<TestObject__c>(s))); 

Returns the following logs

true
true
2
{TestObject__c:{Name=000111, AccEmail__c=email@site.com, Message__c=NEW}}
(TestObject__c:{Name=000111, AccEmail__c=email@site.com, Message__c=NEW}, TestObject__c:{Name=000111, AccEmail__c=email@site.com, Message__c=NEW})
(TestObject__c:{Name=000111, AccEmail__c=email@site.com, Message__c=NEW})

Do you think this a bug? I tried different API versions with the same resulting behavior. If it is not a bug with the set then it might be the system.debug() behavior.

2
  • Are you sure system.debug(s); prints the value which you show in the output. I think you might have copied pasted incorrectly. When the size is 2 is will never print one value. – Nagendra Singh Jan 14 at 12:17
  • There is no copy-paste mistake. This is why I posted the question. – Gerard Sexton Jan 15 at 1:04
2

I personally disagree with your statement that what Salesforce has stated as behaviour is a "poor excuse".

Sets and Maps have complex internal structures that rely on the use of hash codes and equality to identify where to place (or find) a given value in that structure. This allows fast traversal of the structure to determine if the value is held in the Set or Map (key).

If the value placed in the Set or Map (key) is mutable, changing its content will naturally change its hash code and therefore means the stored value is now in the wrong place within that data structure and thus will not be found by a search.

What you see comes from the fact that the first time you insert the value into the Set it has one hash code, but when you insert it a second time it has a new hash code. On first insert the value goes into one place in the structure. On the second insert the value won't be found in the required place in the data structure (it is still there, but now wrongly placed) so the value is added again. This is what you are seeing.

This is not a bug in the Set and Map implementations, but rather a mistake in the way you are using the Set. I.e. it is a bug in your code.

Note that, unfortunately, System.debug of the Set or Map itself (not some value returned from a method on Set or Map) actually causes internal re-evaluation of Set and Map key values and changes behaviour in the surrounding code because of this. That will be why the later debug output changes to only show one entry; the mere action of System.debug printing the Set has caused it to be cleaned up internally, so now you only have one entry. You can read more on this in this previous Q&A.

Because SObjects are implicitly mutable, unless you know for sure that you won't modify such an object you should never use it as a Map key or put it in a Set.

2
  • Thanks for the discussion. Is your comment about internal re-evaluation based on the log output? I tried calling .size() after logging the set, but it is still 2. The list contains 2 elements. Maybe system.debug really only prints unique set items. – Gerard Sexton Jan 15 at 1:31
  • @GerardSexton I clarified that it is the debug of the Set or Map, not some value fetched from it, that causes this issue and pointed you at a previous Q&A that further discusses this situation. – Phil W Jan 15 at 7:55

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.