1

I am using a SObject as a key and mutating it. When I try to get the map value using the sobject, I get null. However, when i serialize the value, I can see the old value stored.

Can anyone clarify on why serialization not showing null value ? Here is the lines i tried.

Account a1 = new Account(Name = 'Hello World');
Map<Account, String> mapAccount = new Map<Account, String>();
mapAccount.put(a1, 'MapString');
insert a1;
System.assert(false, mapAccount +'---'+JSON.serialize(mapAccount));

Output

Line: 5, Column: 1

System.AssertException: Assertion Failed: {Account:{Name=Hello World, Id=0012F00000ZUJW9QAP}=null}---{"Account:{Name=Hello World, Id=0012F00000ZUJW9QAP}":"MapString"}

2
  • I am just wondering how you are even able to compare a boolean with string in assert statement and we don't get any error from apex compiler! Feb 29, 2020 at 14:15
  • What exactly you want to assert in this statement - System.assert(false, mapAccount +'---'+JSON.serialize(mapAccount)); Feb 29, 2020 at 14:16

1 Answer 1

3

A map internally stores the data by hash codes, so it looks like this:

 { "12345" => [value1],
   "23456" => [value2],
   "34567" => [value3, value4]
 }

When retrieving a value via get, it does something like:

 List<Object> valuesInBucket = this.storage[hashCode];
 for(Object value: valuesInBucket) {
   if(value.hashCode.equals(hashCode) && value.equals(key)) {
     return value;
   }
 }
 return null;

As you can see, the hash code is necessary to find the correct area in storage. If you modify the key's hash code, the value can't be found in the old bucket, so the value becomes "lost" inside the internal structure of the Map.

There's a few ways you "might" be able to fix the internal state, such as System.debug(theMap), but this is undocumented and unsupported in all cases. You cannot rely on this fixing the problem.

You must not alter the key's hash code after placing a value in the map, or you will get unreliable behavior.

Note that the values are still in the map, they're just unaccessible via normal methods, including keySet(), values(), get(), and put().

I went in to a lot more detail here, and there's a number of great answers here. All you really need to know is that the hash code must not change or your code may (most likely) fail. Down the path of monkeying with the keys, here be dragons.

3
  • Thanks sfdcfox, so when i try to get the value i get null but when i serialize map then I see the map value as 'MapString'. Shouldn't the serialization show null value as well ?
    – lambad
    Feb 29, 2020 at 15:14
  • @lambad The values are in the map's storage area, just inaccessible via normal methods. The values were not removed just because the key material changed. Serialization (JSON.serialize, System.debug) reads directly from this storage area, which is why it will still return those values.
    – sfdcfox
    Feb 29, 2020 at 15:17
  • got it, thank you very much
    – lambad
    Feb 29, 2020 at 15:45

You must log in to answer this question.

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