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
Mapto turn lookups into constant-time operations. - Small changes make a big difference as your data scales.
💬 Final Tips
- Use
Map<Long, String>instead ofMap<Long, List<String>>if memos are one-to-one. - Always check for
nullwhen 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.
Top comments (0)