2

I am using various fields, including Number or Currency type ones, in two different object types to form a "key" to allow me to generate an "identity" that I can use in a Map in Apex code, which I then use to store and retrieve details relevant to the one type of object AND the other.

These "keys" are implemented as simple Object arrays (since the field types vary), but the fields used at each index are equivalent from the first object type and the second. When I have a Number or Currency type field, these values are represented by Decimal values in Apex.

For (a very contrived) example, I might use the Account's AnnualRevenue field to match with a custom RevenueBreakdown__c's TotalRevenue__c field, both being Currency fields, in order to access data from my Map<Object[], SomeApexClass>.

However, I'm finding that, while the individual values in the key may be the same, the Map is not correctly retrieving the stored data. Any clues why?

2

Apex has a quirk that means the scale in a Decimal is sometimes ignored for equality but is sometimes treated as important. Take the following example:

Decimal a = 10;
Decimal b = 10;

// Simulate the number of decimal places being set differently in the
// two fields in the two objects where these values originate
a = a.setScale(2);
b = b.setScale(3);

Decimal[] aArray = new Decimal[] { a };
Decimal[] bArray = new Decimal[] { b };
Decimal[] bFixedArray = new Decimal[] { b.setScale(a.scale()) };

System.debug('a == b is ' + (a == b) );
System.debug('array a == array b is ' + (aArray.equals(bArray)));
System.debug('array a == array b (fixed) is ' + (aArray.equals(bFixedArray)));

The result of running this is:

a == b is true
array a == array b is false
array a == array b (fixed) is true

It looks like numeric equality operates correctly at the individual Decimal level, but when a Decimal is included in an array, the array equality inexplicably considers both the value AND the scale as being relevant. It is likely this reason that causes this issue.

The Currency or Number fields in the object model must have the same scale (number of decimal places) when they are declared to allow for this sort of usage, or you need to explicitly re-align the scales of the values to be used in these keys.

NB: Array equality in Apex is not checking addresses, but is checking CONTENT. The debug above is using Array.equals but you get the same answers using == between two arrays. From the List documentation (NB: arrays are lists are arrays):

Two lists are equal if their elements are equal and are in the same order. The == operator is used to compare the elements of the lists.

The == operator is equivalent to calling the equals method, so you can call list1.equals(list2); instead of list1 == list2;.

| improve this answer | |
  • Dude, you are comparing Object refrences in aArray == bArray as both are separate instances, == will return false. As both aArray and bArray point to different address in memory. – Pranay Jaiswal Sep 18 '19 at 8:20
  • @PranayJaiswal, but when both have scale 2, its true – salesforce-sas Sep 18 '19 at 8:22
  • @salesforce-sas from Java point of view, comparing arrays compares addresses and never values: repl.it/repls/PeruFrighteningProtocol – Pranay Jaiswal Sep 18 '19 at 8:27
  • @PranayJaiswal.. agreed. I thought you were disagreeing with the array equality inexplicably considers both the value AND the scale as being relevant. When you use array equals method also the results are same. – salesforce-sas Sep 18 '19 at 8:36
  • @PranayJaiswal, sorry mate, you're wrong. Apex is not Java. See my updated answer. – Phil W Sep 18 '19 at 11:18

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.