I ran some benchmark tests based on your two examples. I assumed going into it the difference was going to be slight and the results seem to reflect that. Based on your use case, I don't see a reason to build a map at all since you're most interested in whether the keySet
of the map itself contains a value (related, you can call containsKey
on a map much like contains
on a set). Your most likely performance hit will be in storing all of your map values in memory and not necessarily CPU time in accessing your data structure.
A few notes on my test:
- I'm using a traditional for-loop so that I can traverse the data set 100,000 times. There are performance differences between this loop syntax and the Apex for-in loop syntax.
- I'm using a map with 100 mock values in it. The size of the map or set shouldn't be a factor in performance where CPU is concerned, only in heap being used.
- The if statements are copied from your sample code, only I'm never interested in the loop actually exiting early so the conditions are the inverse so that the loop runs all the way through.
Using Map.get()
Map<String, String> mockMap = new Map<String, String>{
'Land iguana' => 'Conolophus subcristatus',
'Goliath heron' => 'Ardea golieth',
'Pelican, great white' => 'Pelecans onocratalus',
'White-faced whistling duck' => 'Dendrocygna viduata',
'Bottle-nose dolphin' => 'Tursiops truncatus',
'Bee-eater, nubian' => 'Merops nubicus',
'Duck, white-faced whistling' => 'Dendrocygna viduata',
'Galapagos hawk' => 'Buteo galapagoensis',
'Egyptian cobra' => 'Naja haje',
'Snake, carpet' => 'Morelia spilotes variegata',
'Feral rock pigeon' => 'Columba livia',
'Red-capped cardinal' => 'Paroaria gularis',
'Eastern boa constrictor' => 'Acrantophis madagascariensis',
'Eastern grey kangaroo' => 'Macropus giganteus',
'Bear, grizzly' => 'Ursus arctos',
'Duck, white-faced whistling' => 'Dendrocygna viduata',
'Masked booby' => 'Sula dactylatra',
'Squirrel, grey-footed' => 'Paraxerus cepapi',
'Mississippi alligator' => 'Alligator mississippiensis',
'Southern hairy-nosed wombat' => 'Lasiorhinus latifrons',
'Squirrel, eastern fox' => 'Sciurus niger',
'Malleefowl' => 'Leipoa ocellata',
'Heron, giant' => 'Ardea golieth',
'Great white pelican' => 'Pelecans onocratalus',
'Ox, musk' => 'Ovibos moschatus',
'Dusky gull' => 'Larus fuliginosus',
'Black-throated butcher bird' => 'Cracticus nigroagularis',
'African buffalo' => 'Snycerus caffer',
'Palm squirrel' => 'Funambulus pennati',
'Lizard, frilled' => 'Chlamydosaurus kingii',
'Brown capuchin' => 'Cebus apella',
'Monitor, two-banded' => 'Varanus salvator',
'American woodcock' => 'Scolopax minor',
'White-winged dove' => 'Zenaida asiatica',
'Guanaco' => 'Lama guanicoe',
'Crowned eagle' => 'Spizaetus coronatus',
'Long-nosed bandicoot' => 'Perameles nasuta',
'Adouri (unidentified)' => 'unavailable',
'Lesser flamingo' => 'Phoeniconaias minor',
'Wagtail, african pied' => 'Motacilla aguimp',
'Boa, malagasy ground' => 'Acrantophis madagascariensis',
'Red and blue macaw' => 'Ara chloroptera',
'Barbet, crested' => 'Trachyphonus vaillantii',
'Buffalo, american' => 'Bison bison',
'Siskin, pine' => 'Carduelis pinus',
'Boa, columbian rainbow' => 'Epicrates cenchria maurus',
'Cat, kaffir' => 'Felis silvestris lybica',
'Monitor, two-banded' => 'Varanus salvator',
'Swan, trumpeter' => 'Cygnus buccinator',
'Tsessebe' => 'Damaliscus lunatus',
'Mocking cliffchat' => 'Thamnolaea cinnmomeiventris',
'European shelduck' => 'Tadorna tadorna',
'White-tailed jackrabbit' => 'Lepus townsendii',
'Marmot, yellow-bellied' => 'Marmota flaviventris',
'Crab (unidentified)' => 'unavailable',
'Duiker, common' => 'Sylvicapra grimma',
'Hyena, spotted' => 'Crocuta crocuta',
'Pelican, australian' => 'Pelecanus conspicillatus',
'Bird, magnificent frigate' => 'Fregata magnificans',
'Mexican wolf' => 'Canis lupus baileyi',
'Emu' => 'Dromaeus novaehollandiae',
'Barking gecko' => 'Phylurus milli',
'Eastern indigo snake' => 'Drymarchon corias couperi',
'Salmon, sockeye' => 'Oncorhynchus nerka',
'White-fronted bee-eater' => 'Merops bullockoides',
'Antelope, roan' => 'Hippotragus equinus',
'Bent-toed gecko' => 'Cyrtodactylus louisiadensis',
'Sable antelope' => 'Hippotragus niger',
'Tammar wallaby' => 'Macropus eugenii',
'American badger' => 'Taxidea taxus',
'Glider, feathertail' => 'Acrobates pygmaeus',
'Weeper capuchin' => 'Cebus nigrivittatus',
'Civet, small-toothed palm' => 'Arctogalidia trivirgata',
'Coqui partridge' => 'Francolinus coqui',
'White-lipped peccary' => 'Tayassu pecari',
'Red-billed toucan' => 'Ramphastos tucanus',
'Pigeon, wood' => 'Columba palumbus',
'Cockatoo, red-breasted' => 'Eolophus roseicapillus',
'Peacock, indian' => 'Pavo cristatus',
'Carpet python' => 'Morelia spilotes variegata',
'Monkey, black spider' => 'Ateles paniscus',
'Black-backed jackal' => 'Canis mesomelas',
'Lesser mouse lemur' => 'Microcebus murinus',
'Crested barbet' => 'Trachyphonus vaillantii',
'North American red fox' => 'Vulpes vulpes',
'Snake (unidentified)' => 'unavailable',
'Jackal, silver-backed' => 'Canis mesomelas',
'Wolf, timber' => 'Canis lupus lycaon',
'Chuckwalla' => 'Sauromalus obesus',
'Sugar glider' => 'Petaurus breviceps',
'Heron, green-backed' => 'Butorides striatus',
'Blackbird, red-winged' => 'Agelaius phoeniceus',
'Red meerkat' => 'Cynictis penicillata',
'Wood pigeon' => 'Columba palumbus',
'Starling, greater blue-eared' => 'Lamprotornis chalybaeus',
'Giant otter' => 'Pteronura brasiliensis',
'Suricate' => 'Suricata suricatta',
'Flicker, field' => 'Colaptes campestroides',
'Red-cheeked cordon bleu' => 'Uraeginthus bengalus'
};
List<String> commonNames = new List<String>(mockMap.keySet());
Integer commonNamesSize = commonNames.size();
String targetName;
String name;
Integer startTime = Limits.getCpuTime();
for(Integer i = 0; i < 100000; ++i) {
name = commonNames[Math.mod(i, commonNamesSize)];
targetName = mockMap.get(name);
if(targetName == null) {
break;
}
}
System.debug('Elapsed CPU time = ' + String.valueOf(Limits.getCpuTime() - startTime));
Using Set.contains()
// Map instantiation code left out for brevity, see the code block above.
List<String> commonNames = new List<String>(mockMap.keySet());
Set<String> nameSet = mockMap.keySet();
Integer commonNamesSize = commonNames.size();
String targetName;
String name;
Integer startTime = Limits.getCpuTime();
for(Integer i = 0; i < 100000; ++i) {
name = commonNames[Math.mod(i, commonNamesSize)];
if(!nameSet.contains(name)) {
break;
}
}
System.debug('Elapsed CPU time = ' + String.valueOf(Limits.getCpuTime() - startTime));
Results
These code blocks were run in execute anonymous 5 times each.
╔═════╦════════════╦══════════╗
║ Run ║ Map (ms) ║ Set (ms) ║
╠═════╬════════════╬══════════╣
║ 1 ║ 1,238║ 1,234║
║ 2 ║ 1,344║ 1,314║
║ 3 ║ 1,267║ 1,277║
║ 4 ║ 1,481║ 1,242║
║ 5 ║ 1,376║ 1,414║
╠═════╬════════════╬══════════╣
║ Avg ║ 1,341║ 1,296║
╚═════╩════════════╩══════════╝
Set.contains()
wins by about 45ms.
Map
always has a keySet
which is directly tied into theMap
so updates to theSet
update theMap
(I strongly suspect the keySet
is actually theMap
or theMap
's internal data structure simply exposed as aSet
interface). Of course the internal data structure or implementation forMap
andSet
could be different, though it would make sense to share a common core and thus share general performance, ignoring heap allocation aspects.