Lab 09: Collections — List, Set, Map, Queue
Objective
Use Java's Collections Framework — ArrayList, LinkedList, HashSet, TreeSet, HashMap, TreeMap, and Queue — choose the right collection for each use case, and understand performance trade-offs.
Background
The Java Collections Framework provides ready-made implementations of lists, sets, maps, and queues. Choosing the right collection is one of the most impactful decisions you can make: HashSet.contains() is O(1) while ArrayList.contains() is O(n). TreeMap keeps keys sorted automatically. Understanding these trade-offs separates junior from senior developers.
Time
45 minutes
Prerequisites
Lab 06 (OOP — Classes)
Lab 08 (Interfaces)
Tools
Java 21 (Eclipse Temurin)
Docker image:
innozverse-java:latest
Lab Instructions
Step 1: List — ArrayList vs LinkedList
💡
ArrayListvsLinkedList: UseArrayListfor almost everything — it has better cache performance and random access is O(1). UseLinkedListonly when you're adding/removing from both ends frequently (thoughArrayDequeis usually better for that). The "use LinkedList for frequent insertions" advice is outdated for most cases.
📸 Verified Output:
Step 2: Set — HashSet, LinkedHashSet, TreeSet
💡 Choose your Set:
HashSetfor fast membership tests (O(1)),LinkedHashSetwhen insertion order matters,TreeSetfor sorted iteration or range queries (headSet,tailSet,subSet). Never use aListto check "does this item exist" at scale — that's O(n) vs O(1) forHashSet.
📸 Verified Output:
Step 3: Map — HashMap, LinkedHashMap, TreeMap
💡
merge(key, value, fn)is the cleanest way to count occurrences.computeIfAbsent(key, fn)creates the value only if the key is absent — perfect for grouping. Both eliminate verbosecontainsKey()+put()patterns.Map.Entry.comparingByValue()sorts by value in a single expression.
📸 Verified Output:
Step 4: Queue, Deque, PriorityQueue
💡
ArrayDequeis the right choice for both stacks and queues — faster thanLinkedListandStack.PriorityQueueis a min-heap:poll()always returns the smallest element. Reverse the comparator for a max-heap (like a task scheduler where higher priority = runs first).
📸 Verified Output:
Step 5: Iterating Collections
💡 Never use
list.remove(item)inside afor-eachloop — it throwsConcurrentModificationException. UseIterator.remove(),removeIf(), or collect items to remove and callremoveAll()afterward.removeIfwith a lambda is the cleanest modern approach.
📸 Verified Output:
Step 6: Collections Utility Class
💡
Collections.unmodifiableList()wraps a list in a view that throws on mutation. For truly immutable lists, useList.of()(no view — a different implementation).Collections.synchronizedList()adds basic thread safety but doesn't protect compound operations — preferCopyOnWriteArrayListfor concurrent reads.
📸 Verified Output:
Step 7: Performance Comparison
💡 This benchmark shows why data structure choice matters.
HashSet.contains()is O(1) — it computes the hash and checks one bucket.ArrayList.contains()is O(n) — it checks every element. At 100K elements, this is a 100–1000× difference. Always use the right tool.
📸 Verified Output:
(times vary by machine; relative difference is consistent)
Step 8: Real-World — Student Grade Book
💡
LinkedHashMappreserves insertion order — students appear in the order they were added. The stream pipeline then sorts by GPA descending. CombiningMapfor O(1) student lookup,Listfor ordered grades, and streams for reporting is idiomatic modern Java.
📸 Verified Output:
Verification
Summary
You've worked with ArrayList, LinkedHashSet, TreeSet, HashMap, TreeMap, ArrayDeque, PriorityQueue, and Collections utilities. The key takeaway: choose the collection that matches your access pattern — set for uniqueness, map for key lookup, sorted variants for ordered iteration, and queue/deque for processing order.
Further Reading
Last updated
