Given the following code, with two alternative ways to iterate through it,
is there any performance difference between these two methods?
Map<String, Integer> map = new HashMap<String, Integer>(); //populate map //alt. #1 for (String key : map.keySet()) { Integer value = map.get(key); //use key and value } //alt. #2 for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); //use key and value }
I am inclined to think that alt. #2
is the more efficient means of iterating through the entire map
(but I could be wrong)
HashMap is a general purpose Map implementation. It provides a performance of O(1) , while TreeMap provides a performance of O(log(n)) to add, search, and remove items. Hence, HashMap is usually faster.
HashMap, being a hashtable-based implementation, internally uses an array-based data structure to organize its elements according to the hash function. HashMap provides expected constant-time performance O(1) for most operations like add(), remove() and contains(). Therefore, it's significantly faster than a TreeMap.
The reason that HashMap is faster than HashSet is that the HashMap uses the unique keys to access the values. It stores each value with a corresponding key and we can retrieve these values faster using keys during iteration. While HashSet is completely based on objects and therefore retrieval of values is slower.
Your second options is definitely more efficient since you are doing a lookup only once compared to n number of times in the first option.
But, nothing sticks better than trying it out when you can. So here goes -
(Not perfect but good enough to verify assumptions and on my machine anyway)
public static void main(String args[]) { Map<String, Integer> map = new HashMap<String, Integer>(); // populate map int mapSize = 500000; int strLength = 5; for(int i=0;i<mapSize;i++) map.put(RandomStringUtils.random(strLength), RandomUtils.nextInt()); long start = System.currentTimeMillis(); // alt. #1 for (String key : map.keySet()) { Integer value = map.get(key); // use key and value } System.out.println("Alt #1 took "+(System.currentTimeMillis()-start)+" ms"); start = System.currentTimeMillis(); // alt. #2 for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); // use key and value } System.out.println("Alt #2 took "+(System.currentTimeMillis()-start)+" ms"); }
RESULTS (Some interesting ones)
With int mapSize = 5000; int strLength = 5;
Alt #1 took 26 ms
Alt #2 took 20 ms
With int mapSize = 50000; int strLength = 5;
Alt #1 took 32 ms
Alt #2 took 20 ms
With int mapSize = 50000; int strLength = 50;
Alt #1 took 22 ms
Alt #2 took 21 ms
With int mapSize = 50000; int strLength = 500;
Alt #1 took 28 ms
Alt #2 took 23 ms
With int mapSize = 500000; int strLength = 5;
Alt #1 took 92 ms
Alt #2 took 57 ms
...and so on
The second snippet will be slightly faster, since it doesn't need to re-look-up the keys.
All HashMap
iterators call the nextEntry
method, which returns an Entry<K,V>
.
Your first snippet discards the value from the entry (in KeyIterator
), then looks it up again in the dictionary.
Your second snippet uses the key and value directly (from EntryIterator
)
(Both keySet()
and entrySet()
are cheap calls)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With