Why Your Code Might Be Slowing Down for No Apparent Reason?
\nImagine: you've written a perfect algorithm, passed all unit tests, but in production the application suddenly starts "lagging". Most often, the culprit is not the algorithm itself, but how you traverse collections. This is especially true for Java Map — a data structure found in 9 out of 10 enterprise projects.
\nThe question "How do I efficiently iterate over each entry in a Java Map?" on Stack Overflow has gathered tens of thousands of views for a reason. Many developers, even with 2-3 years of experience, use suboptimal approaches. Let's figure out how to traverse a Map correctly, and why one method can be 10 times faster than another.
\n\nThe Anatomy of a Java Map: What's Inside?
\nBefore talking about iteration, let's recall the structure. HashMap is a hash table with buckets. TreeMap is a red-black tree. LinkedHashMap is a hybrid with a doubly linked list. Each of these structures stores key-value pairs, but their traversal methods differ in speed and memory consumption.
The main view collections provided by Map:
\n- \n
keySet()— a set of keys \n values()— a collection of values \n entrySet()— a set of pairs (key-value) \n
It's through these three "windows" that we will look at the data.
\n\nMethod #1: Classic entrySet with for-each (The Most Popular)
\nThis is a basic but very effective pattern. You get access to both the key and the value in a single iteration.
\nMap<String, Integer> map = new HashMap<>();\n// ... filling the map\nfor (Map.Entry<String, Integer> entry : map.entrySet()) {\n String key = entry.getKey();\n Integer value = entry.getValue();\n // your logic\n}\nWhy is this efficient? Because entrySet() returns a view, not a copy of the data. The iterator traverses the internal nodes (Node) of the HashMap, and each call to next() immediately gives both the key and the value. There is no additional hash lookup.
When to use: whenever you need both components of the pair. This is the de facto standard in the Java community.
\n\nMethod #2: keySet + get (An Anti-Pattern to Avoid)
\nMany beginners write code like this:
\nfor (String key : map.keySet()) {\n Integer value = map.get(key);\n // logic\n}\nAt first glance, the code is readable. But internally, HashMap.get() recalculates the key's hash and traverses the collision chain every single time. For 100 elements, this is imperceptible, but for 100,000 — it's a serious degradation. In the worst case (many collisions), the complexity turns from O(n) into O(n²).
\nConclusion: if you need both the key and the value — use entrySet(). If you only need keys — keySet() is ideal.
Method #3: Iterator with Explicit Removal (For Mutations During Traversal)
\nSometimes you need to remove elements from a Map during iteration. A for-each loop will throw a ConcurrentModificationException. This is where the iterator comes to the rescue:
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator();\nwhile (iterator.hasNext()) {\n Map.Entry<String, Integer> entry = iterator.next();\n if (entry.getValue() < 0) {\n iterator.remove(); // safe removal\n }\n}\nThis method is especially useful when you are filtering data "on the fly". The iterator works in O(1) per removal in a HashMap, as it knows the current node.
\n\nMethod #4: forEach with Lambda (Java 8+)
\nStarting with Java 8, Map has its own forEach method:
map.forEach((key, value) -> {\n System.out.println(key + " -> " + value);\n});\nUnder the hood