When writing quick test logic or proof-of-concept code, it’s common for developers to prioritize simplicity over performance. One classic example? Nested loops used for data cross-referencing.
Let’s explore a real-world Java example, analyze the problem, and then optimize it the right way.
🚨 The Naive Approach: Simple, but Costly
Suppose you're iterating through a list of users and matching each with corresponding memos by userId
. Here’s how it often looks:
for (User user : userTestList) {
Long userId = user.getUserId();
for (UserMemo userMemo : userMemoTestList) {
if (userId.equals(userMemo.getUserId())) {
String content = userMemo.getContent();
System.out.println("Processing mock content..." + content);
}
}
}
❌ Performance Problem:
- Time complexity is O(n × m).
- If each list has 1,000 items, you’re performing 1 million comparisons.
- Doesn’t scale well as list sizes increase.
✅ The Optimized Approach: Use a HashMap
Instead of scanning the memo list for every user, index the memos by userId
to enable constant-time lookups.
🔧 Optimized Java Code
// Step 1: Build a map from userId to memo content list
Map<Long, List<String>> memoMap = new HashMap<>();
for (UserMemo userMemo : userMemoTestList) {
memoMap
.computeIfAbsent(userMemo.getUserId(), k -> new ArrayList<>())
.add(userMemo.getContent());
}
// Step 2: Iterate through users and lookup memo content
for (User user : userTestList) {
List<String> contents = memoMap.get(user.getUserId());
if (contents != null) {
for (String content : contents) {
System.out.println("Processing mock content..." + content);
}
}
}
✅ Advantages:
- Time complexity drops to O(n + m)
- Efficient for large datasets
- Keeps your code readable and maintainable
🧪 Optional: Benchmarking It
To see the actual performance gain, wrap each version in timing logic:
long start = System.nanoTime();
// ... run the loop logic
long end = System.nanoTime();
System.out.println("Elapsed time (ms): " + (end - start) / 1_000_000);
Or, use a profiler like JVisualVM or Java Mission Control for deeper insight.
🧵 Key Takeaway
If your code needs to cross-reference two lists based on a shared key:
- Avoid nested loops for every lookup.
- Use a
Map
to turn lookups into constant-time operations. - Small changes make a big difference as your data scales.
💬 Final Tips
- Use
Map
instead ofMap
if memos are one-to-one.> - Always check for
null
when using.get()
from the map. - Don't forget to measure before and after — it's satisfying to see that performance win.
📌 TL;DR Table
Approach | Time Complexity | Readability | Scalability |
---|---|---|---|
Nested Loop | O(n × m) | ✅ Simple | ❌ Slow |
Map Lookup | O(n + m) | ✅ Simple | ✅ Fast |
That’s it! Clean code doesn’t have to be slow—and performant code doesn’t have to be ugly.