-3

I don't understand the behavior of the map. Here is my case :

Map<ContractLineItem, Decimal> cliWithNbrMap = data(otherParameters);
for( ContractLineItem cli : cliWithNbrMap.keySet()){
    system.debug(  cliWithNbrMap.containsKey(cli));
}

The Map is not empty, i get the first value And get false for other values This is a snippet of my code

for(Integer i=1; i<= woNbr ; i++){
            List<ContractLineItem> prestationOfWO = getPrestationsByWONbr(i, prestationsWithWONbr);
            Map<ContractLineItem,Decimal> prestationsOfWOWithVEPoints = new Map<ContractLineItem,Decimal>();
            for(ContractLineItem prestation : prestationOfWO){
                prestationsOfWOWithVEPoints.put(prestation, prestationsWithVEPoints.get(prestation));
            }
            prestationsWithRank.putAll(getPrestationsWithRank(prestationsOfWOWithVEPoints));
        }
Map<Integer, Decimal> passageNbrWithduree = getDureeListAfterSurBooking (TauxReferenceList,BouclePrestation, getWODuree(prestationsWithRank.get(BouclePrestation),BouclePrestation.CLI_DureeVisite__c),nbrPassages);
private static Map<ContractLineItem,Integer> getPrestationsWithRank(Map<ContractLineItem,Decimal> prestationsWithPointsVE){
        Map<ContractLineItem,Integer> prestationsWithRank = new Map<ContractLineItem,Integer>();
        Map<Integer,Decimal> rankAndPointsVE = new Map<Integer,Decimal>();
        //get the rank according to points VE
        if (prestationsWithPointsVE.size()>0){
            List<Decimal> sortedPointsVE = sortDescending(prestationsWithPointsVE.values());
            for(Integer i=1 ; i<= sortedPointsVE.size(); i++){
                rankAndPointsVE.put(i,sortedPointsVE[i-1]);
            }
        } 
        for(ContractLineItem prestation : prestationsWithPointsVE.keySet()){
                prestationsWithRank.put(prestation, getRankByPointsVE(prestationsWithPointsVE.get(prestation),rankAndPointsVE));
                System.debug('prestation: ' + getRankByPointsVE(prestationsWithPointsVE.get(prestation),rankAndPointsVE));
        }
        System.debug('prestationsWithRank: ' + prestationsWithRank.size());
        return prestationsWithRank;
    }
7
  • 4
    Well the map is empty. And you should avoid ever using any SObject as a map key. Use its Id instead.
    – Adrian Larson
    Jan 13 at 17:29
  • As per Salesforce documentation Map keys can be of SObject type as well please find the documentation from Salesforce "Map keys and values can be of any data type—primitive types, collections, sObjects, user-defined types, and built-in Apex types." Jan 13 at 17:37
  • 1
    As my parents often said, and as I eventually learned, just because you can do something doesn't mean that you should.
    – Moonpie
    Jan 13 at 17:39
  • Is this snippet the exact code that you're running? If it is, then I cannot reproduce what you're seeing. Given that the map is empty, keySet() should return an empty list, which means that the system.debug(); is never executed. Also, there are some applications where using SObjects as a map key are appropriate, but this doesn't look to be one of them.
    – Derek F
    Jan 13 at 17:44
  • 1
    Just because it can be a map key does not mean it should be, or that you will use it properly. Using them makes mistakes extremely easy to make.
    – Adrian Larson
    Jan 13 at 18:27
1

If you modify an sObject after it is placed in the key, its hashCode() value changes, and the value will become "lost" in the map. There are some hacks to fix this, but you should not rely on such hacks.

Invalid

Map<ContractLineItem, Decimal> cliWithNbrMap = new Map<ContractLineItem, Decimal>();
ContractLineItem key = new ContractLineItem();
cliWithNbpMap.put(key, 1);
// We are altering the hashCode
key.Quantity = 2;
for( ContractLineItem cli : cliWithNbrMap.keySet()){
  system.debug(  cliWithNbrMap.containsKey(cli)); // key not found
}

Valid

Map<ContractLineItem, Decimal> cliWithNbrMap = new Map<ContractLineItem, Decimal>();
ContractLineItem key = new ContractLineItem();
key.Quantity = 2;
cliWithNbpMap.put(key, 1);
// We did not change the hashCode
for( ContractLineItem cli : cliWithNbrMap.keySet()){
  system.debug(  cliWithNbrMap.containsKey(cli)); // Key is found
}

Notably, this behavior is expected if you do something like:

Map<ContractLineItem, Decimal> cliWithNbrMap = new Map<ContractLineItem, Decimal>();
ContractLineItem key = new ContractLineItem(...);
cliWithNbpMap.put(key, 1);
// We modified the record's Id field...
insert key;
for( ContractLineItem cli : cliWithNbrMap.keySet()){
  system.debug(  cliWithNbrMap.containsKey(cli)); // Key not found
}

Because the act of doing an insert changes the Id field, which in turn changes the object's hashCode().

In cases like this, it may be prudent to instead use two lists:

ContractLineItem[] items = new ContractLineItem[0];
Decimal[] values = new Decimal[0];
// populate both lists at once
insert items;
for(Integer index = 0, size = items.size(); index < size; index++) {
  ContractLineItem item = items[index];
  Decimal value = values[index];
}
5
  • In my case the sObject (key) doesn't change this is why i putted it in key side
    – M A H
    Jan 13 at 18:13
  • @MAH You haven't provided enough information in your question to provide any other solution. If you have a bug on your hands, we would need to see more precise code. As far as I'm aware, the only scenario that can cause this is by messing with the keys after placing them in the map.
    – sfdcfox
    Jan 13 at 18:15
  • i edited my post to show my code
    – M A H
    Jan 13 at 18:21
  • @MAH I'm guessing your problem is in getPrestationsWithRank. Oh, also, I forgot to mention in my previous comment, but changing your debug log level can affect the output of the containsKey method. See salesforce.stackexchange.com/q/254891/2984
    – sfdcfox
    Jan 13 at 18:27
  • the method getPrestationsWithRank does the job well. I have the expected values. Thank you for this article, I will delete all my sysdebug
    – M A H
    Jan 13 at 18:38

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