You’ve Been Picking Java Collections by Guessing. Let’s Fix That: The Power Moves (Maps & Queues)
Welcome back! In Part 1, we looked at the structural foundations of the Java Collections Framework — breaking down why ArrayList is your…
You’ve Been Picking Java Collections by Guessing. Let’s Fix That: The Power Moves (Maps & Queues)
Welcome back! In Part 1, we looked at the structural foundations of the Java Collections Framework — breaking down why ArrayList is your default notebook and how HashSet uses room numbers to sniff out duplicates in the blink of an eye. (If you missed it, you can catch up on Part 1 here).
Today, we’re taking the training wheels off. We’re diving into the heavy lifters of real-world enterprise applications: the Map Family (the absolute kings of fast lookups) and Queues (the machinery behind background processing). To wrap it all up, I’m giving you a strict, zero-guesswork Decision Guide and the two gold rules of code safety you cannot afford to break. Let’s jump back in.
The Map Family — When You Need Key-Value Pairs
A Map is not technically a Collection , but it’s part of the Collections Framework and one of the most-used data structures in real applications. A Map stores data as key-value pairs — like a dictionary where you look up a word (key) to find its definition (value). Each key must be unique.
Real-world examples of Maps:
- Username → User account details
- Product ID → Price
- Word → Number of times it appears in a document
HashMap — The Ultra-Fast Lookup Table
The Analogy: A Restaurant Menu with Section Numbers
Instead of flipping through every page of a menu to find “Margherita Pizza,” imagine the menu has a system: salads are in section 1, pizzas in section 2, desserts in section 3. You know what you want, you know its section — you go straight there.
A HashMap works the same way. It uses the key’s hashCode() to determine where to store the value. Looking something up is near-instant regardless of how many items the map contains.
Map<String, Integer> wordCount = new HashMap<>();
wordCount.put("apple", 5);
wordCount.put("banana", 3);
wordCount.put("cherry", 8);
int count = wordCount.get("banana"); // Instantly returns 3
wordCount.put("apple", 6); // Updates the existing value
System.out.println(wordCount.containsKey("mango")); // false
Important notes:
- Keys must be unique. Putting a new value with an existing key replaces the old value.
- HashMap allows one
nullkey and multiplenullvalues. - Order of elements is not guaranteed.
Performance at a glance:

When to use it: Whenever you need to associate one thing with another and look it up quickly — which is most of the time. Caches, configuration settings, counting frequencies.
LinkedHashMap — HashMap With Memory
The Analogy: A Notebook Where You Jot Things in Order
Same as a HashMap, but it also keeps a linked list of entries in the order they were inserted. When you iterate, you always get items back in the order you put them in.
Map<String, String> capitals = new LinkedHashMap<>();
capitals.put("India", "New Delhi");
capitals.put("France", "Paris");
capitals.put("Japan", "Tokyo");
// Always prints in insertion order: India, France, Japan
for (String country : capitals.keySet()) {
System.out.println(country);
}
When to use it: When you need all the speed of a HashMap but also need to preserve insertion order — for example, building an ordered config or maintaining a consistent display order.
TreeMap — The Always-Sorted Map
The Analogy: A Financial Ledger Sorted by Date
A bank’s transaction ledger keeps every entry sorted by date. At any point, you can quickly ask: “Show me all transactions from March 1st to March 15th.” A sorted structure makes range queries like this efficient.
Map<Integer, String> employeeById = new TreeMap<>();
employeeById.put(1003, "Alice");
employeeById.put(1001, "Charlie");
employeeById.put(1002, "Bob");
// Always prints in key order: 1001-Charlie, 1002-Bob, 1003-Alice
for (Map.Entry<Integer, String> entry : employeeById.entrySet()) {
System.out.println(entry.getKey() + ": " + entry.getValue());
}
// Range query: employees with IDs between 1001 and 1002 (inclusive)
Map<Integer, String> subset = employeeById.subMap(1001, true, 1002, true);
When to use it: When you need a map whose keys are always in sorted order, or when you need to find all entries within a range of keys.
Queue and Deque — Processing in Order
A Queue follows the FIFO rule: First In, First Out. Think of a line at a coffee shop — the first person who queued gets served first.
A Deque(Double-Ended Queue, pronounced “deck”) lets you add and remove from both ends.

// Queue example: customer service line
Queue<String> line = new LinkedList<>();
line.offer("Alice"); // Alice joins the line
line.offer("Bob");
line.offer("Charlie");
String nextCustomer = line.poll(); // Returns and removes "Alice"
System.out.println(nextCustomer); // "Alice"
// Deque example: used as a Stack (Last In, First Out)
Deque<String> stack = new ArrayDeque<>();
stack.push("Page 1");
stack.push("Page 2");
stack.push("Page 3");
String lastVisited = stack.pop(); // Returns "Page 3" - browser back button!
When to use it:
- Queue / LinkedList — Task scheduling, BFS graph traversal, processing jobs in order
- ArrayDeque — Whenever you need a Stack or a fast double-ended queue (faster than Stack class and LinkedList)
The Decision Guide
Use this as your starting point whenever you need to pick a collection.
What do you need to store?
│
├── A sequence of items (order matters, duplicates OK)?
│ → Use a LIST
│ │
│ ├── Mostly reading / accessing by index?
│ │ → ArrayList ✅
│ │
│ └── Mostly adding/removing from the front?
│ → LinkedList (or ArrayDeque)
│
├── A group of unique items (no duplicates)?
│ → Use a SET
│ │
│ ├── Just need fast add/check, don't care about order?
│ │ → HashSet ✅
│ │
│ ├── Need items always sorted?
│ │ → TreeSet
│ │
│ └── Need insertion order preserved?
│ → LinkedHashSet
│
├── Key → Value pairs (lookup by key)?
│ → Use a MAP
│ │
│ ├── Just need fast lookup, don't care about order?
│ │ → HashMap ✅
│ │
│ ├── Need keys always sorted, or range queries?
│ │ → TreeMap
│ │
│ └── Need insertion order preserved?
│ → LinkedHashMap
│
└── Process items in a specific sequence?
├── First-in, first-out (like a queue)?
│ → LinkedList as Queue, or ArrayDeque
└── Last-in, first-out (like a stack)?
→ ArrayDeque as Stack
Practical Examples You’ll Actually Use
Example 1 — Counting Word Frequencies
String text = "the cat sat on the mat the cat";
String[] words = text.split(" ");
Map<String, Integer> frequency = new HashMap<>();
for (String word : words) {
// getOrDefault returns 0 if the word isn't in the map yet
frequency.put(word, frequency.getOrDefault(word, 0) + 1);
}
System.out.println(frequency);
// {the=3, cat=2, sat=1, on=1, mat=1}
Example 2 — Removing Duplicates While Preserving Order
List<String> withDuplicates = List.of("banana", "apple", "banana", "cherry", "apple");
// Creates an immutable list: [banana, apple, banana, cherry, apple]\
Set<String> unique = new LinkedHashSet<>(withDuplicates);
// LinkedHashSet drops duplicates AND remembers insertion order
// Result: {banana, apple, cherry} ← second banana and apple are silently ignored
List<String> result = new ArrayList<>(unique);
// Converts back to a List if you need index access
// Result: [banana, apple, cherry]
System.out.println(result); // [banana, apple, cherry] ✅
Example 3 — A Simple Task Queue
Queue<String> taskQueue = new LinkedList<>();
taskQueue.offer("Send email report");
taskQueue.offer("Run database backup");
taskQueue.offer("Clear cache");
while (!taskQueue.isEmpty()) {
String task = taskQueue.poll();
System.out.println("Processing: " + task);
}
// Processing: Send email report
// Processing: Run database backup
// Processing: Clear cache
Two Rules to Always Follow
Rule 1: Declare using the Interface, not the Implementation.
// ✅ Correct
List<String> names = new ArrayList<>();
Map<String, Integer> scores = new HashMap<>();
// ❌ Avoid - locks you into the implementation
ArrayList<String> names = new ArrayList<>();
Rule 2: If you store custom objects in a HashSet or as HashMap keys, override both equals() and hashCode().
public class Student {
String name;
int id;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Student)) return false;
Student s = (Student) o;
return id == s.id && Objects.equals(name, s.name);
}
@Override
public int hashCode() {
return Objects.hash(name, id);
}
}
Without this, two Student objects with the same name and ID would be treated as different objects by any hash-based collection.

Where to Go Next
Once you’re comfortable with these basics:
- Explore Collections utility class — it provides helper methods like sort(), shuffle(), unmodifiableList(), and synchronizedList().
- Learn about thread-safe collections — ConcurrentHashMap and CopyOnWriteArrayList for multi-threaded environments.
- Understand Big-O notation more deeply — the performance trade-offs between collections will make much more sense once you’re fluent in complexity analysis.
- Practice by building — reimplement a simple HashMap from scratch using arrays. Nothing teaches you the internals better than building it yourself.
The goal isn’t to memorize the API. It’s to understand what problem each collection solves. Once that clicks, you’ll choose the right tool every time not by looking it up, but by reasoning about it.
메타데이터
- post_id
- fc397fd6d310
- slug
- youve-been-picking-java-collections-by-guessing-let-s-fix-that-the-power-moves-maps-queues-fc397fd6d310
- url
- https://medium.com/@r-ragul/youve-been-picking-java-collections-by-guessing-let-s-fix-that-the-power-moves-maps-queues-fc397fd6d310
- canonical_url
- https://medium.com/@r-ragul/youve-been-picking-java-collections-by-guessing-let-s-fix-that-the-power-moves-maps-queues-fc397fd6d310
- author_url
- https://medium.com/@r-ragul
- status
- ok
- fetched_at
- 2026-06-15 20:49:13