DEV Community

Cover image for 🧠 Optimizing Nested Loops in Java: A Real-World Use Case
araf
araf

Posted on • Edited on

🧠 Optimizing Nested Loops in Java: A Real-World Use Case

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); } } } 
Enter fullscreen mode Exit fullscreen mode

❌ 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); } } } 
Enter fullscreen mode Exit fullscreen mode

✅ 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); 
Enter fullscreen mode Exit fullscreen mode

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<Long, String> instead of Map<Long, List<String>> 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.

Top comments (0)