The canteen that outgrew its array
Riya's canteen app started with String[] orders = new String[50];. On fest day there were 312 orders, the same customer was counted three times in “unique visitors”, the token queue was served out of order, and finding a dish's price meant looping over two parallel arrays.
Sir, my array overflowed, my visitor count is wrong, and searching prices is slow. Do I need to write my own data structures?
No. Java already has them, tested by millions of programs. You have four different jobs here, so you need four different containers: a List for orders, a Set for unique visitors, a Queue for tokens and a Map for prices.
Four new things to learn?
Four interfaces, each a promise about behaviour, and a few classes that keep those promises in different ways. Learn the promises first; picking the class becomes easy.
What is the Collections Framework?
The Java Collections Framework (in java.util, since Java 1.2) is a unified architecture for storing and manipulating groups of objects. It consists of interfaces (Collection, List, Set, Queue, Deque, Map and their sorted variants), implementations (such as ArrayList, LinkedList, HashSet, TreeSet, ArrayDeque, PriorityQueue, HashMap, TreeMap) and algorithms (static methods in Collections such as sort, max and frequency). Collections hold object references only, are generic, and (except for legacy classes) grow dynamically.
A toolbox of ready-made containers for groups of objects. Each container type promises a behaviour (ordered? duplicates? first-in-first-out? lookup by key?), and you pick the one whose promise fits your job.
Words you'll meet in this lesson
add, remove, size, contains.List.of(…), Map.of(…).The family tree
Iterable
└─ Collection
├─ List → ArrayList, LinkedList ordered, index, duplicates OK
├─ Set → HashSet, LinkedHashSet no duplicates
│ └─ SortedSet → TreeSet sorted, no duplicates
└─ Queue → PriorityQueue process in order of priority
└─ Deque → ArrayDeque, LinkedList add/remove at both ends (queue or stack)
Map (NOT a Collection) → HashMap, LinkedHashMap key → value, keys unique
└─ SortedMap → TreeMap keys sortedExam tip: Map does not extend Collection; it's a separate hierarchy. And Collection (interface, the root of List/Set/Queue) is not Collections (a utility class with static methods like sort). Both are classic MCQ traps.
Four promises, and which class to pick
List
Ordered by position, duplicates allowed, get(i). Default: ArrayList.
Set
No duplicates. HashSet (fast), LinkedHashSet (keeps insertion order), TreeSet (sorted).
unique IDs, tagsQueue / Deque
Process in order. ArrayDeque (FIFO queue or LIFO stack), PriorityQueue (smallest first).
tokens, tasks, undoMap
Key → value, unique keys. HashMap (fast), LinkedHashMap (insertion order), TreeMap (sorted keys).
prices, counts, cachesThe same data in four containers
import java.util.*;
public class Main {
public static void main(String[] args) {
String[] visits = {"Riya", "Kabir", "Riya", "Om", "Riya"};
List<String> log = new ArrayList<>(Arrays.asList(visits)); // keeps everything
Set<String> unique = new HashSet<>(log); // drops repeats
Set<String> sorted = new TreeSet<>(log); // drops repeats, sorts
Queue<String> line = new ArrayDeque<>(log); // first in, first out
Map<String, Integer> count = new TreeMap<>();
for (String v : log) count.put(v, count.getOrDefault(v, 0) + 1);
System.out.println("List : " + log);
System.out.println("Set : " + unique.size() + " unique");
System.out.println("Tree : " + sorted);
System.out.println("Queue: serve " + line.poll() + ", next " + line.peek());
System.out.println("Map : " + count);
}
}Choosing quickly
| You need… | Use | Key operations (typical cost) |
|---|---|---|
| A sequence, read by position | ArrayList | get(i) O(1), add at end O(1) amortised |
| Many inserts/removes at the ends | ArrayDeque | offer/poll/push/pop O(1) |
| “Have I seen this before?” | HashSet | add/contains O(1) average |
| Unique and sorted | TreeSet | add/contains O(log n) |
| Always take the smallest/most urgent | PriorityQueue | offer/poll O(log n) |
| Find a value by key | HashMap | get/put O(1) average |
| Keys in sorted order | TreeMap | get/put O(log n) |
Fixed, unmodifiable and immutable
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> fixed = Arrays.asList(1, 2, 3); // fixed size: set OK, add not
fixed.set(0, 9);
System.out.println(fixed);
List<Integer> frozen = List.of(1, 2, 3); // Java 9+: fully immutable
try {
frozen.add(4);
} catch (UnsupportedOperationException e) {
System.out.println("List.of can't change");
}
System.out.println(Collections.max(frozen) + " " + Collections.frequency(List.of("a", "b", "a"), "a"));
}
}Rule of thumb: declare variables with the interface type (List<String> log = new ArrayList<>()). Your code then depends only on the promise, and you can switch to another class later by changing one word.
Picking a collection in four questions
Do I look things up by a key?
Yes → a Map. Price by dish, student by roll number, count by word. Then choose: any order (HashMap), insertion order (LinkedHashMap), sorted keys (TreeMap).Must every element be unique?
Yes → a Set. The set usesequals/hashCode(Lesson 10.3) orcompareTo(TreeSet) to spot duplicates.Do I process items in arrival or priority order?
Yes → a Queue/Deque. FIFO tokens:ArrayDequewithoffer/poll. Undo stack:ArrayDequewithpush/pop. Most urgent first:PriorityQueue.Otherwise: a List
ArrayListcovers most cases: fast reads by index and fast adds at the end. Lesson 11.4 shows whenLinkedListis (rarely) better.Declare the interface, create the class
The variable's type is the interface; the object is the class. Behind the scenes the variable is just a reference to aMap<String, Integer> price = new HashMap<>();HashMapobject on the heap, which itself holds references to the key and value objects.
One family, many members: both views
Left: code creating different collections. Right: where each class sits in the interface family.
Collectionis the root promise shared by lists, sets and queues.- A
Listvariable holding anArrayListobject. - A
Setvariable holding aHashSet. - A
Dequeholding anArrayDeque: usable as a queue or a stack. - A
Mapholding aHashMap. Collections.sort: a static helper in the utility classCollections.
CollectionextendsIterable, which is why every list, set and queue works in a for-each loop.Listis a child ofCollection;ArrayListandLinkedListare classes that implement it.Setalso extendsCollection; HashSet, LinkedHashSet and TreeSet implement it.Queueand its childDeque: ArrayDeque and PriorityQueue (and LinkedList, which is both a List and a Deque).Mapis not under Collection. It's a separate family of key–value containers.Collectionsisn't in the tree at all: it's a class full of static algorithms (sort, max, frequency…) that work on collections.
Collections in real systems
Nearly every Java program uses collections; the skill is choosing the one whose promise matches the job.
Watch it with an example: a bank's customer-service desk
One morning at a bank branch, four collections work together: a queue of tokens, a set of customers seen today, a map of account balances, and a list of the day's log.
- Customers take tokens T1, T2, T3. The desk's
Queueholds them in arrival order. poll()serves T1: Riya. Her balance comes from theMapin one lookup, she's added to theSet, and the visit is appended to theList.- T2: Kabir. Same steps: map lookup, set add, list append.
- T3 is Riya again. The log grows (it's a List), but
set.add("Riya")returns false: she's already counted. - End of the morning: the list says 3 visits, the set says 2 different customers. Each collection answered a different question. (Simplified: real branches use a database, not memory.)
1. Shopping carts and feeds: List
A cart, a timeline of posts, a train's list of stations: order matters and repeats are allowed. ArrayList is the usual choice.
2. De-duplication and membership: Set
Unique visitors, blocked card numbers, already-processed transaction IDs (so a retried payment isn't charged twice).
3. Work waiting its turn: Queue
Support tickets, print jobs, messages waiting to be sent, emergency patients by severity (PriorityQueue), and browser back/undo stacks (Deque).
4. Lookups and caches: Map
IFSC code → branch, user ID → session, product ID → stock. Caches that avoid slow database calls are maps at heart.
Examples are simplified. Multi-threaded systems use the concurrent versions (ConcurrentHashMap, BlockingQueue), covered in Module 12.
Canteen day, four collections
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, Integer> price = new HashMap<>();
price.put("chai", 15);
price.put("dosa", 60);
price.put("vada", 25);
List<String> orders = new ArrayList<>();
Set<String> customers = new HashSet<>();
Queue<String> tokens = new ArrayDeque<>();
String[][] today = {{"Riya", "chai"}, {"Kabir", "dosa"}, {"Riya", "vada"}};
for (String[] t : today) {
orders.add(t[1]);
customers.add(t[0]);
tokens.offer(t[0] + ":" + t[1]);
}
int total = 0;
for (String dish : orders) total += price.get(dish);
System.out.println("Orders: " + orders);
System.out.println("Customers: " + customers.size());
System.out.println("Serve first: " + tokens.poll());
System.out.println("Takings: ₹" + total);
}
}Map<String, Integer> priceLookup by dish name:price.get("dosa").Set<String> customersRiya is added twice but counted once.Queue<String> tokensofferjoins the back,pollserves the front.- Interface on the leftEach variable is declared with its interface; the class is chosen once, on the right.
Same names, three containers, three results
This trace is recorded from the real JVM. Watch the heap: the list keeps every visit, the set ignores the repeat, and the map turns the repeat into a count.
Worked examples: real problems, solved and traced in memory
Three everyday problems, each solved by picking the right collection. Watch how differently they grow on the heap.
Unique visitors from a page log
Problem statement
A website logs one user ID per page view. Count unique visitors and print them in the order they first appeared.
["u7", "u3", "u7", "u9", "u3"]
3 unique: [u7, u3, u9]
Approach
- A
LinkedHashSetignores repeats and remembers first-arrival order. - A helper
uniqueVisitors(String[] log)adds every ID. - Print the size and the set.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Serve support tickets in order
Problem statement
Tickets T101, T102, T103 arrive. An agent serves two, then T104 arrives. Print who is served and who is still waiting.
T101, T102, T103; serve 2; T104 arrives
Serving T101 Serving T102 Waiting: [T103, T104]
Approach
- Use
ArrayDequeas a FIFO queue:offerat the back,pollfrom the front. - Serve with
poll(), which returns and removes the oldest ticket. - Print what remains.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Stock left after the day's sales
Problem statement
A shop starts with pens 10, books 4. Sales today: pen, book, pen. Update stock with a Map and print it.
stock {pen=10, book=4}; sales pen, book, pen{book=3, pen=8}Approach
- A
TreeMapso the report prints items alphabetically. - A helper
sell(stock, item)reduces the count by one. - Print the map.
Solution: run it live
What happens in memory, step by step
Recorded from the real JVM: press Next to execute one line at a time. Watch method calls push and pop frames on the stack, and objects (numbered #1, #2 …) appear on the heap.
Check yourself
Quick revision
Lesson 11.1 in one screen
- Framework = interfaces + implementations + algorithms (
Collectionsclass), injava.util. - Iterable → Collection → List (ordered, duplicates), Set (unique), Queue/Deque (processing order). Map is separate.
- Defaults: ArrayList, HashSet, ArrayDeque, HashMap. Sorted: TreeSet, TreeMap. Insertion order: LinkedHashSet, LinkedHashMap. Priority: PriorityQueue.
- Choose by asking: lookup by key? unique? processing order? else List.
Arrays.asList= fixed size;List.of/Map.of= immutable;Collections.unmodifiableList= read-only view.- Declare with the interface type; collections hold references only.
Interview and viva questions
Why use collections instead of arrays?
Arrays have a fixed size and no built-in operations for searching, de-duplicating or key lookup. Collections grow automatically, offer rich, tested operations and data structures with known performance, and work through common interfaces so algorithms like sorting work on many of them.
List vs Set vs Map?
A List is an ordered sequence that allows duplicates and access by index. A Set holds unique elements (order depends on the implementation). A Map stores key–value pairs with unique keys and isn't a Collection.
What is the difference between Arrays.asList, List.of and Collections.unmodifiableList?
Arrays.asList is a fixed-size view backed by the array (set allowed, add/remove not). List.of is truly immutable and rejects nulls. Collections.unmodifiableList is a read-only view of another list: if the original changes, the view shows it.
Are the standard collections thread-safe?
No. ArrayList, HashMap and friends are not synchronised. Use java.util.concurrent classes such as ConcurrentHashMap and CopyOnWriteArrayList, or synchronise access yourself (Module 12). Legacy Vector and Hashtable are synchronised but rarely used today.
What is the root interface of the framework?
Collection is the root of List, Set and Queue; it itself extends Iterable, which is what makes the for-each loop work. Map has its own root, Map.
Practice problems
Pick the collection whose promise fits the job. The last three are business tasks.
Remove duplicate marks
Marks {78, 91, 78, 64, 91}: print the distinct marks in sorted order.
{78, 91, 78, 64, 91}[64, 78, 91]
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
Integer[] marks = {78, 91, 78, 64, 91};
Set<Integer> distinct = new TreeSet<>(Arrays.asList(marks));
System.out.println(distinct);
}
}How it works: a TreeSet drops duplicates and keeps the rest sorted.
Reverse a word with a stack
Push each character of "JAVA" onto an ArrayDeque stack, then pop them to print it reversed.
"JAVA"
AVAJ
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
Deque<Character> stack = new ArrayDeque<>();
for (char c : "JAVA".toCharArray()) stack.push(c);
StringBuilder out = new StringBuilder();
while (!stack.isEmpty()) out.append(stack.pop());
System.out.println(out);
}
}How it works: a stack is last-in, first-out, so popping returns characters in reverse order.
Collections utility methods
For the list [5, 2, 9, 2], print the max, the min, how often 2 appears, and the list sorted.
[5, 2, 9, 2]
9 2 2 [2, 2, 5, 9]
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
List<Integer> nums = new ArrayList<>(List.of(5, 2, 9, 2));
System.out.println(Collections.max(nums) + " " + Collections.min(nums) + " " + Collections.frequency(nums, 2));
Collections.sort(nums);
System.out.println(nums);
}
}How it works: Collections (with an s) provides static algorithms. sort needs a modifiable list, hence the copy into an ArrayList.
First repeated order ID
Find the first order ID that appears twice in ["A1", "B7", "C3", "B7", "A1"].
["A1", "B7", "C3", "B7", "A1"]
First repeat: B7
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
String[] ids = {"A1", "B7", "C3", "B7", "A1"};
Set<String> seen = new HashSet<>();
for (String id : ids) {
if (!seen.add(id)) {
System.out.println("First repeat: " + id);
break;
}
}
}
}How it works: add returns false when the element is already present, so the first false is the first repeat. O(n) overall.
Most urgent tasks first
Tasks with priorities 4, 1, 3, 2 arrive. Print them in order of priority using a PriorityQueue.
4, 1, 3, 2
[1, 2, 3, 4]
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
PriorityQueue<Integer> pq = new PriorityQueue<>(List.of(4, 1, 3, 2));
List<Integer> order = new ArrayList<>();
while (!pq.isEmpty()) order.add(pq.poll());
System.out.println(order);
}
}How it works: poll always removes the smallest element. Printing the queue directly wouldn't show sorted order; only polling does.
Branch lookup by IFSC
Store three IFSC codes with their branch names in a map, then look up SBIN0001234 and an unknown code.
"SBIN0001234", "XXXX0000000"
SBI Kharghar Unknown IFSC
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
Map<String, String> branch = new HashMap<>();
branch.put("SBIN0001234", "SBI Kharghar");
branch.put("HDFC0000240", "HDFC Vashi");
branch.put("ICIC0001520", "ICICI Panvel");
System.out.println(branch.get("SBIN0001234"));
System.out.println(branch.getOrDefault("XXXX0000000", "Unknown IFSC"));
}
}How it works: a HashMap finds the value for a key in constant time on average, no matter how many branches are stored.
Daily sales by category, sorted
Orders: (Books, 450), (Toys, 300), (Books, 200), (Electronics, 1200). Print total per category in alphabetical order.
4 orders
{Books=650, Electronics=1200, Toys=300}Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
String[] cat = {"Books", "Toys", "Books", "Electronics"};
int[] amt = {450, 300, 200, 1200};
Map<String, Integer> totals = new TreeMap<>();
for (int i = 0; i < cat.length; i++) {
totals.put(cat[i], totals.getOrDefault(cat[i], 0) + amt[i]);
}
System.out.println(totals);
}
}How it works: the map aggregates by key; TreeMap keeps keys sorted for the report.
Recently viewed products
A shop shows the last 3 products a user viewed, newest first, without repeats. Views: P1, P2, P3, P2, P4.
P1, P2, P3, P2, P4
[P4, P2, P3]
Show solution and explanation
import java.util.*;
public class Main {
public static void main(String[] args) {
Deque<String> recent = new ArrayDeque<>();
for (String p : new String[]{"P1", "P2", "P3", "P2", "P4"}) {
recent.remove(p); // drop an older view of the same product
recent.addFirst(p); // newest at the front
if (recent.size() > 3) recent.removeLast();
}
System.out.println(recent);
}
}How it works: a Deque lets us add at the front and trim from the back. Removing an existing copy first avoids repeats.
Real-world scenario questions (asked in company interviews)
Collection questions in interviews are usually “which one would you use and why?” The best answers name the promise you need, then the class.
Don't charge twice
A payment service sometimes receives the same request twice because of network retries. Each request has a txnId. Which collection would you use to make sure each transaction is processed once, and how?
The interviewer is checking: using a Set for membership and its add() result.
Show a strong answer
- A
Set<String>of processed IDs.if (!processed.add(txnId)) return "duplicate";:addreturns false if the ID was already there, in O(1) on average. - In a real multi-server system, keep this state in a shared store (database unique key or a cache), since each server has its own memory, and use a concurrent set within one server.
- Expire old IDs so the set doesn't grow forever.
Avoid: scanning an ArrayList with contains. It's O(n) per request and gets slower every day.
Emergency room queue
Patients arrive at a hospital emergency desk with a severity from 1 (critical) to 5. Doctors must always see the most critical patient next, and among equals, whoever came first. Which collection?
The interviewer is checking: PriorityQueue and tie-breaking.
Show a strong answer
- A
PriorityQueue<Patient>with a comparator on severity, then arrival time (a counter or timestamp) to break ties, since PriorityQueue alone isn't stable. offerandpollare O(log n);peekshows the next patient.- If severity can change while waiting, remove and re-insert the patient.
Avoid: sorting a List after every arrival. It works, but costs O(n log n) each time.
Category totals in alphabetical order
Finance wants daily sales per product category, printed in alphabetical order of category. Orders arrive in random order. What would you use?
The interviewer is checking: Map for aggregation, TreeMap for sorted keys.
Show a strong answer
Map<String, Long> totals = new TreeMap<>();thentotals.merge(category, amount, Long::sum)for each order.- The TreeMap keeps categories sorted, so printing it gives the report order directly.
- If order only matters at the end and data is huge, a HashMap plus one sort at the end is also fine.
Avoid: two parallel lists of names and totals. Keeping them in sync is error-prone.
A getter that leaks internals
Order.getItems() returns the order's internal ArrayList. Another team calls getItems().clear() and empties real orders. How do you prevent this?
The interviewer is checking: unmodifiable views and defensive copies.
Show a strong answer
- Return
Collections.unmodifiableList(items)(a read-only view) orList.copyOf(items)(an immutable snapshot). - Offer intention-revealing methods like
addItem/removeItemthat enforce business rules. - Declare the return type as
List, notArrayList, so you're free to change the implementation.
Avoid: documenting “please don't modify”. Make it impossible instead.