tl;dr:
You're gonna have a loop one way or another, be it explicit or implicit.
The long version
It could at least in in theory be done, but neither the Map
class nor any Object nor SObject support that out of the box. You'd need to roll that logic yourself.
The .put()/.add()
and .get()
methods of Map and Set rely on two methods that every object in Apex has, hashCode()
and equals()
. Using Custom Types in Map Keys and Sets goes over the requirements that those methods should meet (for map and set operations to behave correctly), and you'd add them to your Apex class that you're using to represent a date range.
The tricky (and potentially impossible) bit is going to be implementing .hashCode()
. If the hashcode of the thing you pass to .get()
doesn't match any of those in your map, .get()
stops there. You'd need to come up with some way to turn your date range into a single integer, and for a single date in that range to return the same integer.
Since a single date can be part of an infinite number of ranges, it's impossible to satisfy both of the conditions above (date range hash => single integer, single date => same integer) unless the hashcode is a constant.
After .hashcode()
is run, then .equals()
is run. Implementing .equals()
would be simple enough. It'd just be you comparing your single given date against the start/end dates to see if it is in the range.
From some brief experimenting, Maps (and Sets) work like this:
- The hashcode is computed (
.hashCode()
is called) for the object being passed to .add()
/.put()
or .get()
- For items being added/put, the record is added to an internal list (ordered iterable collection) under the computed hashcode
- If multiple items have the same hashcode, the new item is appended to the internal list (so the list can have a size > 1)
- The internal list is retrieved for the hashcode, and
.equals()
is called on the items in sequence
- If
equals()
returns true, the current item in the internal list is returned
- Else, if there are still more items in the internal list, we move to the next one and call
.equals()
- When there are no more items, and
.equals()
returns false, .get()
returns null
So in the end, in the general case, you're not going to be able to avoid looping over each date range and doing a comparison. The only difference here is going to be whether you explicitly write that loop, or if you let Apex set up/run the loop on your behalf (with you providing the body of the loop between the .hashCode()
and .equals()
methods).
If you know that your date ranges will have some specific properties (e.g. they will always be in a single year and never cross a year boundary, as in your given example), then it could be possible to have a non-constant hashcode. It'd take having some number of items in your map to make .get()
perform quicker than just looping over the date ranges yourself, but it would be possible to get better performance. The best (average) performance should be when the hashcodes for your date ranges is uniformly distributed.
For those interested, here's the anonymous apex I used:
public class MyClass {
public Integer hash;
public Integer hash2;
public integer hashCode(){
system.debug('in hashCode');
return hash;
}
public Boolean equals(Object other){
system.debug('in equals, other hash2 = ' + ((MyClass)other).hash2);
return hash == ((MyClass)other).hash && hash2 == ((MyClass)other).hash2;
}
}
MyClass inst1 = new MyClass();
inst1.hash = 1;
inst1.hash2 = 1;
MyClass inst2 = new MyClass();
inst2.hash = 1;
inst2.hash2 = 2;
MyClass inst3 = new MyClass();
inst3.hash = 1;
inst3.hash2 = 3;
MyClass inst4 = new MyClass();
inst4.hash = 1;
inst4.hash2 = 4;
MyClass inst5 = new MyClass();
inst5.hash = 1;
inst5.hash2 = 5;
MyClass inst6 = new MyClass();
inst6.hash = 1;
inst6.hash2 = 6;
Set<MyClass> mySet = new Set<MyClass>{
inst1,
inst2,
inst3,
inst4,
inst6
};
system.debug('calling get');
//system.debug(mySet.get(inst1));
system.debug(mySet.get(inst6));