Java Collections Hierarchy
Java Collections Hierarchy
The Java Collections Framework is not a flat list of unrelated classes. It is a carefully designed hierarchy of interfaces and implementations where every concrete class — ArrayList, HashMap, TreeSet, ArrayDeque — fills a specific slot defined by the interface it implements. Once you understand the hierarchy, you stop memorising individual classes and start reasoning about which one fits a given problem.
This article maps every interface and implementation, shows you what each level of the hierarchy guarantees, and gives you working examples for each branch.
What Is the Collections Hierarchy?
The Collections hierarchy is the inheritance and implementation structure that organises all collection types in java.util and java.util.concurrent. Every collection either extends an interface or implements one — and those interfaces form a tree rooted at java.lang.Iterable.
The full hierarchy has two independent branches: the Collection branch for single-element containers, and the Map branch for key-value associations. Map does not extend Collection — the two branches are siblings, not parent and child.
COLLECTION BRANCH
=================
java.lang.Iterable<E> ← supports for-each loop
└── java.util.Collection<E> ← root of all single-element collections
│
├── java.util.List<E> ← ordered, allows duplicates, index access
│ ├── ArrayList (resizable array — default choice)
│ ├── LinkedList (doubly-linked nodes)
│ ├── Vector (legacy, synchronised)
│ └── Stack (legacy LIFO — extends Vector)
│
├── java.util.Set<E> ← unique elements, no duplicates
│ ├── HashSet (hash table, no order)
│ ├── LinkedHashSet (hash table + insertion order)
│ └── SortedSet<E>
│ └── NavigableSet<E>
│ └── TreeSet (Red-Black tree, sorted order)
│
└── java.util.Queue<E> ← ordered for processing, typically FIFO
├── PriorityQueue (min-heap, priority order)
└── java.util.Deque<E> ← double-ended queue, both-end access
├── ArrayDeque (resizable array, modern default)
└── LinkedList (also implements List and Deque)
MAP BRANCH (separate hierarchy — does NOT extend Collection)
============================================================
java.util.Map<K,V> ← key-value pairs, unique keys
├── HashMap (hash table, no key order)
├── LinkedHashMap (hash table + insertion order)
├── Hashtable (legacy, synchronised)
└── SortedMap<K,V>
└── NavigableMap<K,V>
└── TreeMap (Red-Black tree, sorted keys)
The interfaces define contracts — what operations a collection supports. The concrete classes define implementation — how those operations work internally and what performance they provide.
The Interface Layers — What Each One Adds
Layer 1 — Iterable
java.lang.Iterable<E> is the root. It lives in java.lang so it is automatically imported. It declares one method: iterator(). Any class that implements Iterable can be used in a Java enhanced for-each loop. Every Collection extends Iterable, so every collection works with for-each automatically.
1// File: IterableDemo.java
2
3import java.util.ArrayList;
4import java.util.Iterator;
5import java.util.List;
6
7public class IterableDemo {
8
9 public static void main(String[] args) {
10
11 List<String> languages = new ArrayList<>();
12 languages.add("Java");
13 languages.add("Python");
14 languages.add("Go");
15
16 // for-each works because ArrayList implements Iterable
17 System.out.println("=== for-each loop (via Iterable) ===");
18 for (String lang : languages) {
19 System.out.println(" " + lang);
20 }
21
22 // Under the hood, for-each compiles to this iterator call
23 System.out.println("=== explicit iterator (what for-each becomes) ===");
24 Iterator<String> it = languages.iterator();
25 while (it.hasNext()) {
26 System.out.println(" " + it.next());
27 }
28 }
29}Output:
=== for-each loop (via Iterable) ===
Java
Python
Go
=== explicit iterator (what for-each becomes) ===
Java
Python
Go
Layer 2 — Collection
java.util.Collection<E> extends Iterable and adds the core group-management methods that every collection supports: add(), remove(), contains(), size(), isEmpty(), clear(), addAll(), removeAll(), retainAll(), and toArray().
Declaring a method parameter as Collection<T> makes it accept any List, Set, or Queue — the widest useful type for read and basic modification.
1// File: CollectionInterfaceDemo.java
2
3import java.util.ArrayList;
4import java.util.Collection;
5import java.util.HashSet;
6import java.util.List;
7import java.util.Set;
8
9public class CollectionInterfaceDemo {
10
11 // Works with ANY Collection — ArrayList, HashSet, TreeSet, LinkedList...
12 static void printSummary(Collection<String> items) {
13 System.out.println(" Size : " + items.size());
14 System.out.println(" Empty? : " + items.isEmpty());
15 System.out.println(" Has Java?: " + items.contains("Java"));
16 System.out.println(" Contents : " + items);
17 }
18
19 public static void main(String[] args) {
20
21 List<String> list = new ArrayList<>();
22 list.add("Java"); list.add("Spring"); list.add("Java"); // duplicate allowed
23
24 Set<String> set = new HashSet<>();
25 set.add("Java"); set.add("Spring"); set.add("Java"); // duplicate rejected
26
27 System.out.println("=== ArrayList passed as Collection ===");
28 printSummary(list);
29
30 System.out.println("=== HashSet passed as Collection ===");
31 printSummary(set);
32
33 // Core Collection methods
34 System.out.println("\n=== addAll and removeAll ===");
35 Collection<String> base = new ArrayList<>(List.of("A", "B", "C", "D", "E"));
36 Collection<String> remove = List.of("B", "D");
37 base.removeAll(remove);
38 System.out.println(" After removeAll(B,D): " + base);
39
40 Collection<String> keep = List.of("A", "C");
41 base.retainAll(keep);
42 System.out.println(" After retainAll(A,C): " + base);
43 }
44}Output:
=== ArrayList passed as Collection ===
Size : 3
Empty? : false
Has Java?: true
Contents : [Java, Spring, Java]
=== HashSet passed as Collection ===
Size : 2
Empty? : false
Has Java?: true
Contents : [Spring, Java]
=== addAll and removeAll ===
After removeAll(B,D): [A, C, E]
After retainAll(A,C): [A, C]
Layer 3a — List
List<E> extends Collection and adds the contract for ordered, indexed access. It introduces get(int index), set(int index, E element), add(int index, E element), remove(int index), indexOf(Object o), subList(int from, int to), and listIterator(). The ordering guarantee and duplicate permission are what distinguish List from Set.
1// File: ListHierarchyDemo.java
2
3import java.util.ArrayList;
4import java.util.LinkedList;
5import java.util.List;
6
7public class ListHierarchyDemo {
8
9 public static void main(String[] args) {
10
11 // Both ArrayList and LinkedList fulfil the List contract
12 List<String> arrayList = new ArrayList<>(List.of("Flipkart", "Swiggy", "Zomato"));
13 List<String> linkedList = new LinkedList<>(List.of("Flipkart", "Swiggy", "Zomato"));
14
15 // List-specific operations (not on Collection)
16 System.out.println("=== List-specific operations ===");
17 System.out.println("get(1) on ArrayList : " + arrayList.get(1)); // O(1)
18 System.out.println("get(1) on LinkedList : " + linkedList.get(1)); // O(n)
19 System.out.println("indexOf(Swiggy) : " + arrayList.indexOf("Swiggy"));
20
21 arrayList.set(0, "Meesho");
22 System.out.println("After set(0, Meesho) : " + arrayList);
23
24 List<String> sub = arrayList.subList(0, 2); // live view of indices 0 and 1
25 System.out.println("subList(0,2) : " + sub);
26
27 // Both preserve insertion order and allow duplicates
28 System.out.println("\n=== Duplicates and order preserved ===");
29 arrayList.add("Swiggy");
30 arrayList.add("Swiggy");
31 System.out.println("ArrayList with duplicates: " + arrayList);
32 }
33}Output:
=== List-specific operations ===
get(1) on ArrayList : Swiggy
get(1) on LinkedList : Swiggy
indexOf(Swiggy) : 1
After set(0, Meesho) : [Meesho, Swiggy, Zomato]
subList(0,2) : [Meesho, Swiggy]
=== Duplicates and order preserved ===
ArrayList with duplicates: [Meesho, Swiggy, Zomato, Swiggy, Swiggy]
Layer 3b — Set
Set<E> extends Collection and adds the uniqueness guarantee. It introduces no new methods beyond Collection — the contract change is purely behavioural: add() returns false and does nothing if the element already exists. The SortedSet sub-interface adds first(), last(), headSet(), tailSet(), and subSet(). NavigableSet extends SortedSet further with floor(), ceiling(), lower(), higher(), and descendingSet().
1// File: SetHierarchyDemo.java
2
3import java.util.HashSet;
4import java.util.LinkedHashSet;
5import java.util.NavigableSet;
6import java.util.Set;
7import java.util.TreeSet;
8
9public class SetHierarchyDemo {
10
11 public static void main(String[] args) {
12
13 System.out.println("=== HashSet — fastest, no order ===");
14 Set<String> hashSet = new HashSet<>();
15 hashSet.add("Mumbai"); hashSet.add("Delhi"); hashSet.add("Bengaluru");
16 hashSet.add("Mumbai"); // duplicate — no effect
17 System.out.println("Contents : " + hashSet); // order unpredictable
18 System.out.println("add(Mumbai) again: " + hashSet.add("Mumbai")); // false
19
20 System.out.println("\n=== LinkedHashSet — no duplicates, insertion order ===");
21 Set<String> linkedHashSet = new LinkedHashSet<>();
22 linkedHashSet.add("Mumbai"); linkedHashSet.add("Delhi"); linkedHashSet.add("Bengaluru");
23 linkedHashSet.add("Mumbai"); // ignored
24 System.out.println("Contents : " + linkedHashSet); // insertion order preserved
25
26 System.out.println("\n=== TreeSet — no duplicates, sorted order ===");
27 NavigableSet<Integer> treeSet = new TreeSet<>();
28 treeSet.add(50); treeSet.add(20); treeSet.add(80); treeSet.add(10); treeSet.add(60);
29 System.out.println("Contents : " + treeSet); // always sorted ascending
30 System.out.println("first() : " + treeSet.first());
31 System.out.println("last() : " + treeSet.last());
32 System.out.println("floor(55) : " + treeSet.floor(55)); // largest <= 55
33 System.out.println("ceiling(55) : " + treeSet.ceiling(55)); // smallest >= 55
34 System.out.println("headSet(50) : " + treeSet.headSet(50)); // elements < 50
35 System.out.println("tailSet(50) : " + treeSet.tailSet(50)); // elements >= 50
36 }
37}Output:
=== HashSet — fastest, no order ===
Contents : [Delhi, Mumbai, Bengaluru]
add(Mumbai) again: false
=== LinkedHashSet — no duplicates, insertion order ===
Contents : [Mumbai, Delhi, Bengaluru]
=== TreeSet — no duplicates, sorted order ===
Contents : [10, 20, 50, 60, 80]
first() : 10
last() : 80
floor(55) : 50
ceiling(55) : 60
headSet(50) : [10, 20]
tailSet(50) : [50, 60, 80]
Layer 3c — Queue and Deque
Queue<E> extends Collection and models a processing line. It adds two method pairs: add()/offer() (insert at tail — offer returns false on failure instead of throwing), and remove()/poll() (remove from head — poll returns null instead of throwing), plus element()/peek() (inspect head without removal). Deque extends Queue and adds operations on both ends.
1// File: QueueDequeDemo.java
2
3import java.util.ArrayDeque;
4import java.util.Deque;
5import java.util.PriorityQueue;
6import java.util.Queue;
7
8public class QueueDequeDemo {
9
10 public static void main(String[] args) {
11
12 System.out.println("=== ArrayDeque as Queue — FIFO ===");
13 Queue<String> ticketQueue = new ArrayDeque<>();
14 ticketQueue.offer("TICKET-001");
15 ticketQueue.offer("TICKET-002");
16 ticketQueue.offer("TICKET-003");
17
18 System.out.println("Queue : " + ticketQueue);
19 System.out.println("peek() : " + ticketQueue.peek()); // head, no removal
20 System.out.println("poll() : " + ticketQueue.poll()); // remove head
21 System.out.println("Remaining : " + ticketQueue);
22
23 System.out.println("\n=== ArrayDeque as Stack — LIFO (Deque) ===");
24 Deque<String> browserHistory = new ArrayDeque<>();
25 browserHistory.push("/home"); // addFirst
26 browserHistory.push("/products");
27 browserHistory.push("/cart");
28
29 System.out.println("History : " + browserHistory);
30 System.out.println("peek() : " + browserHistory.peek()); // most recent
31 System.out.println("pop() : " + browserHistory.pop()); // go back
32 System.out.println("History : " + browserHistory);
33
34 System.out.println("\n=== PriorityQueue — processes smallest first ===");
35 Queue<Integer> urgencyLevels = new PriorityQueue<>();
36 urgencyLevels.offer(3); urgencyLevels.offer(1); urgencyLevels.offer(5);
37 urgencyLevels.offer(2); urgencyLevels.offer(4);
38
39 System.out.print("Processing order: ");
40 while (!urgencyLevels.isEmpty()) {
41 System.out.print(urgencyLevels.poll() + " ");
42 }
43 System.out.println();
44 }
45}Output:
=== ArrayDeque as Queue — FIFO ===
Queue : [TICKET-001, TICKET-002, TICKET-003]
peek() : TICKET-001
poll() : TICKET-001
Remaining : [TICKET-002, TICKET-003]
=== ArrayDeque as Stack — LIFO (Deque) ===
History : [/cart, /products, /home]
peek() : /cart
pop() : /cart
History : [/products, /home]
=== PriorityQueue — processes smallest first ===
Processing order: 1 2 3 4 5
Layer 3d — Map (Separate Branch)
Map<K,V> does not extend Collection. It defines its own contract: unique keys mapped to values. Core methods: put(K,V), get(K), remove(K), containsKey(K), containsValue(V), keySet(), values(), entrySet(), size(), isEmpty(). SortedMap adds firstKey(), lastKey(), headMap(), tailMap(). NavigableMap extends SortedMap with floorKey(), ceilingKey(), and range views.
1// File: MapHierarchyDemo.java
2
3import java.util.HashMap;
4import java.util.LinkedHashMap;
5import java.util.Map;
6import java.util.NavigableMap;
7import java.util.TreeMap;
8
9public class MapHierarchyDemo {
10
11 public static void main(String[] args) {
12
13 System.out.println("=== HashMap — fastest, no key order ===");
14 Map<String, Integer> hashMap = new HashMap<>();
15 hashMap.put("Razorpay", 2014); hashMap.put("PhonePe", 2015);
16 hashMap.put("Paytm", 2010); hashMap.put("CRED", 2018);
17 System.out.println("Contents : " + hashMap); // key order unpredictable
18 System.out.println("get(PhonePe) : " + hashMap.get("PhonePe"));
19 System.out.println("put replaces : " + hashMap.put("CRED", 2018)); // returns old value
20
21 System.out.println("\n=== LinkedHashMap — insertion order preserved ===");
22 Map<String, Integer> linkedHashMap = new LinkedHashMap<>();
23 linkedHashMap.put("Razorpay", 2014); linkedHashMap.put("PhonePe", 2015);
24 linkedHashMap.put("Paytm", 2010); linkedHashMap.put("CRED", 2018);
25 System.out.println("Contents : " + linkedHashMap); // insertion order
26
27 System.out.println("\n=== TreeMap — keys sorted, NavigableMap methods ===");
28 NavigableMap<String, Integer> treeMap = new TreeMap<>();
29 treeMap.put("Razorpay", 2014); treeMap.put("PhonePe", 2015);
30 treeMap.put("Paytm", 2010); treeMap.put("CRED", 2018);
31 System.out.println("Sorted keys : " + treeMap); // alphabetical
32 System.out.println("firstKey() : " + treeMap.firstKey());
33 System.out.println("lastKey() : " + treeMap.lastKey());
34 System.out.println("floorKey(P) : " + treeMap.floorKey("P")); // largest key <= "P"
35 System.out.println("ceilingKey(P): " + treeMap.ceilingKey("P")); // smallest key >= "P"
36 System.out.println("headMap(P) : " + treeMap.headMap("P")); // keys < "P"
37 }
38}Output:
=== HashMap — fastest, no key order ===
Contents : {Razorpay=2014, Paytm=2010, CRED=2018, PhonePe=2015}
get(PhonePe) : 2015
put replaces : 2018
=== LinkedHashMap — insertion order preserved ===
Contents : {Razorpay=2014, PhonePe=2015, Paytm=2010, CRED=2018}
=== TreeMap — keys sorted, NavigableMap methods ===
Sorted keys : {CRED=2018, Paytm=2010, PhonePe=2015, Razorpay=2014}
firstKey() : CRED
lastKey() : Razorpay
floorKey(P) : Paytm
ceilingKey(P): Paytm
headMap(P) : {CRED=2018}
When to Use Each Level of the Hierarchy
Choosing the right level of the hierarchy prevents two common mistakes: being too specific (coupling code to ArrayList when List or Collection is sufficient) and being too vague (using Collection when the caller genuinely needs index access).
Use as a VARIABLE TYPE or PARAMETER TYPE: Iterable<T> — only when a method needs to iterate (for-each only) Collection<T> — when a method needs add/remove/contains/size + iteration List<T> — when callers need index access, ordering, or subList Set<T> — when the contract is uniqueness and no ordering Queue<T> — when callers use offer/poll/peek FIFO semantics Deque<T> — when both-end access is required Map<K,V> — when key-value association is the contract SortedSet<T> — when sorted iteration and range operations are needed NavigableSet<T> — when floor/ceiling/headSet/tailSet are needed SortedMap<K,V> — when sorted key iteration is needed NavigableMap<K,V>— when floor/ceiling/headMap/tailMap are needed Use as an IMPLEMENTATION (right side of assignment only): ArrayList — default List: random access, bulk append, sequential scan LinkedList — List + Deque: frequent head/tail insert-delete HashSet — default Set: fastest add/contains/remove, no order LinkedHashSet — Set with insertion order TreeSet — Set with sorted order or range navigation ArrayDeque — default Queue/Deque/Stack: fast both-end operations PriorityQueue — Queue with priority ordering (min-heap) HashMap — default Map: fastest put/get/remove, no key order LinkedHashMap — Map with insertion order (or LRU with access order) TreeMap — Map with sorted key order or range navigation
Internal Working of Each Branch
The diagram below shows how each major implementation stores data internally.
INTERNAL STRUCTURE COMPARISON
ArrayList (Object[]):
┌────┬────┬────┬────┬────┬────┬────┬────┬────┬────┐
│ A │ B │ C │null│null│null│null│null│null│null│
└────┴────┴────┴────┴────┴────┴────┴────┴────┴────┘
index: 0 1 2 9
size=3, capacity=10 — grows 1.5x when full
LinkedList (doubly-linked nodes):
null ← [prev|A|next] ↔ [prev|B|next] ↔ [prev|C|next] → null
↑ ↑
first last
O(1) at head/tail, O(n) at index
HashSet / HashMap (hash table — array of buckets):
table[0]: null
table[3]: Node("Java") → null
table[5]: Node("Go") → null
table[7]: Node("Rust") → Node("Python") → null ← collision chain
hashCode(key) → bucket index → equals() for exact match
O(1) average, degrades to O(n) on all-same-bucket collisions
Java 8+: converts chains > 8 to Red-Black tree (O(log n) worst case)
TreeSet / TreeMap (Red-Black tree):
30(B)
/ \
20(R) 50(R)
/ \ / \
10(B) 25(B)40(B) 60(B)
All keys in BST order — in-order traversal always sorted
Guaranteed O(log n) for all operations regardless of insertion order
ArrayDeque (circular array — head and tail move instead of shifting):
array: [null | "B" | "C" | "D" | null | null]
↑ ↑
head tail
addFirst moves head left; addLast moves tail right
Wraps around when end reached — no shifting needed
Real-World Example — Meesho Seller Analytics Platform
A seller analytics platform needs different access patterns simultaneously: ordered transaction history, fast unique-seller lookup, revenue totals by category, and a processing queue for pending payouts. Each requirement maps to a different level of the hierarchy.
1// File: SellerTransaction.java
2
3public class SellerTransaction {
4
5 private final String txnId;
6 private final String sellerId;
7 private final String category;
8 private final double amount;
9 private final String status;
10
11 public SellerTransaction(String txnId, String sellerId,
12 String category, double amount, String status) {
13 this.txnId = txnId;
14 this.sellerId = sellerId;
15 this.category = category;
16 this.amount = amount;
17 this.status = status;
18 }
19
20 public String getTxnId() { return txnId; }
21 public String getSellerId() { return sellerId; }
22 public String getCategory() { return category; }
23 public double getAmount() { return amount; }
24 public String getStatus() { return status; }
25
26 @Override
27 public String toString() {
28 return String.format("[%s] seller=%-8s cat=%-10s Rs.%7.2f %s",
29 txnId, sellerId, category, amount, status);
30 }
31}1// File: SellerAnalytics.java
2
3import java.util.ArrayDeque;
4import java.util.ArrayList;
5import java.util.Collection;
6import java.util.HashMap;
7import java.util.HashSet;
8import java.util.LinkedHashMap;
9import java.util.List;
10import java.util.Map;
11import java.util.Queue;
12import java.util.Set;
13import java.util.TreeMap;
14
15public class SellerAnalytics {
16
17 // List — transaction history in arrival order (ArrayList: index access for display)
18 private final List<SellerTransaction> history = new ArrayList<>();
19
20 // Set — unique seller IDs (HashSet: O(1) membership check)
21 private final Set<String> activeSellers = new HashSet<>();
22
23 // Map — revenue by category (HashMap: O(1) accumulation per key)
24 private final Map<String, Double> revenueByCategory = new HashMap<>();
25
26 // NavigableMap — revenue by category, sorted for ranked report (TreeMap)
27 private final Map<String, Double> rankedRevenue = new TreeMap<>();
28
29 // Queue — pending payout transactions awaiting processing (FIFO)
30 private final Queue<SellerTransaction> payoutQueue = new ArrayDeque<>();
31
32 // LinkedHashMap — most recently active sellers, insertion order preserved
33 private final Map<String, String> recentActivity = new LinkedHashMap<>();
34
35 public void record(SellerTransaction txn) {
36 history.add(txn);
37 activeSellers.add(txn.getSellerId());
38 revenueByCategory.merge(txn.getCategory(), txn.getAmount(), Double::sum);
39 rankedRevenue.merge(txn.getCategory(), txn.getAmount(), Double::sum);
40
41 if ("PENDING_PAYOUT".equals(txn.getStatus())) {
42 payoutQueue.offer(txn);
43 }
44 recentActivity.put(txn.getSellerId(), txn.getTxnId());
45 }
46
47 // Collection parameter — accepts List, Set, Queue — any collection will do here
48 private void printSection(String title, Collection<?> items) {
49 System.out.println("\n--- " + title + " ---");
50 items.forEach(item -> System.out.println(" " + item));
51 }
52
53 public void printReport() {
54 System.out.println("╔══════════════════════════════════════════════╗");
55 System.out.println("║ MEESHO SELLER ANALYTICS REPORT ║");
56 System.out.println("╚══════════════════════════════════════════════╝");
57
58 printSection("Transaction History (List — insertion order)", history);
59
60 System.out.println("\n--- Active Sellers (Set — unique, no order) ---");
61 System.out.println(" " + activeSellers);
62 System.out.println(" Unique sellers: " + activeSellers.size());
63
64 System.out.println("\n--- Revenue by Category (HashMap — fast accumulation) ---");
65 revenueByCategory.forEach((cat, rev) ->
66 System.out.printf(" %-12s Rs.%.2f%n", cat, rev));
67
68 System.out.println("\n--- Revenue Ranked (TreeMap — sorted keys) ---");
69 rankedRevenue.forEach((cat, rev) ->
70 System.out.printf(" %-12s Rs.%.2f%n", cat, rev));
71
72 System.out.println("\n--- Pending Payouts (Queue — FIFO processing) ---");
73 System.out.println(" Queued: " + payoutQueue.size());
74
75 System.out.println("\n--- Recent Activity (LinkedHashMap — insertion order) ---");
76 recentActivity.forEach((seller, txn) ->
77 System.out.printf(" %-10s last txn: %s%n", seller, txn));
78 }
79
80 public void processNextPayout() {
81 SellerTransaction next = payoutQueue.poll();
82 if (next != null) {
83 System.out.println(" Payout processed: " + next);
84 } else {
85 System.out.println(" No pending payouts.");
86 }
87 }
88
89 public static void main(String[] args) {
90
91 SellerAnalytics analytics = new SellerAnalytics();
92
93 analytics.record(new SellerTransaction("T001", "S-PRIYA", "Fashion", 2499.00, "SUCCESS"));
94 analytics.record(new SellerTransaction("T002", "S-ROHAN", "Electronics", 8999.00, "PENDING_PAYOUT"));
95 analytics.record(new SellerTransaction("T003", "S-PRIYA", "Fashion", 1299.00, "PENDING_PAYOUT"));
96 analytics.record(new SellerTransaction("T004", "S-ANANYA", "HomeDecor", 3499.00, "SUCCESS"));
97 analytics.record(new SellerTransaction("T005", "S-KARAN", "Electronics", 5499.00, "SUCCESS"));
98 analytics.record(new SellerTransaction("T006", "S-ROHAN", "Electronics", 2999.00, "PENDING_PAYOUT"));
99
100 analytics.printReport();
101
102 System.out.println("\n--- Processing payouts (Queue.poll — FIFO) ---");
103 analytics.processNextPayout();
104 analytics.processNextPayout();
105 analytics.processNextPayout();
106 }
107}Output:
╔══════════════════════════════════════════════╗
║ MEESHO SELLER ANALYTICS REPORT ║
╚══════════════════════════════════════════════╝
--- Transaction History (List — insertion order) ---
[T001] seller=S-PRIYA cat=Fashion Rs. 2499.00 SUCCESS
[T002] seller=S-ROHAN cat=Electronics Rs. 8999.00 PENDING_PAYOUT
[T003] seller=S-PRIYA cat=Fashion Rs. 1299.00 PENDING_PAYOUT
[T004] seller=S-ANANYA cat=HomeDecor Rs. 3499.00 SUCCESS
[T005] seller=S-KARAN cat=Electronics Rs. 5499.00 SUCCESS
[T006] seller=S-ROHAN cat=Electronics Rs. 2999.00 PENDING_PAYOUT
--- Active Sellers (Set — unique, no order) ---
[S-KARAN, S-PRIYA, S-ROHAN, S-ANANYA]
Unique sellers: 4
--- Revenue by Category (HashMap — fast accumulation) ---
Fashion Rs.3798.00
Electronics Rs.17497.00
HomeDecor Rs.3499.00
--- Revenue Ranked (TreeMap — sorted keys) ---
Electronics Rs.17497.00
Fashion Rs.3798.00
HomeDecor Rs.3499.00
--- Pending Payouts (Queue — FIFO processing) ---
Queued: 3
--- Recent Activity (LinkedHashMap — insertion order) ---
S-PRIYA last txn: T003
S-ROHAN last txn: T006
S-ANANYA last txn: T004
S-KARAN last txn: T005
--- Processing payouts (Queue.poll — FIFO) ---
Payout processed: [T002] seller=S-ROHAN cat=Electronics Rs. 8999.00 PENDING_PAYOUT
Payout processed: [T003] seller=S-PRIYA cat=Fashion Rs. 1299.00 PENDING_PAYOUT
Payout processed: [T006] seller=S-ROHAN cat=Electronics Rs. 2999.00 PENDING_PAYOUT
The example uses six different collection types from four different branches of the hierarchy — each chosen for the access pattern it must support, not for familiarity.
Performance Considerations
| Interface | Implementation | get / lookup | add | remove | contains | Order |
|---|---|---|---|---|---|---|
| List | ArrayList | O(1) by index | O(1) amort | O(n) shift | O(n) scan | Insertion |
| List | LinkedList | O(n) traverse | O(1) ends | O(1) ends | O(n) scan | Insertion |
| Set | HashSet | — | O(1) avg | O(1) avg | O(1) avg | None |
| Set | LinkedHashSet | — | O(1) avg | O(1) avg | O(1) avg | Insertion |
| Set | TreeSet | — | O(log n) | O(log n) | O(log n) | Sorted |
| Queue | ArrayDeque | O(1) ends | O(1) amort | O(1) ends | O(n) | FIFO/LIFO |
| Queue | PriorityQueue | O(1) min peek | O(log n) | O(log n) | O(n) | Priority |
| Map | HashMap | O(1) avg | O(1) avg | O(1) avg | O(1) avg key | None |
| Map | LinkedHashMap | O(1) avg | O(1) avg | O(1) avg | O(1) avg key | Insertion |
| Map | TreeMap | O(log n) | O(log n) | O(log n) | O(log n) | Sorted |
Thread safety: All standard implementations above are not thread-safe. For concurrent use: ConcurrentHashMap for concurrent Map, CopyOnWriteArrayList for read-heavy concurrent List, ConcurrentLinkedQueue for concurrent Queue, Collections.synchronizedXxx() wrappers for simple single-lock access.
Best Practices
Declare variables and parameters using the widest interface that covers your operations. List<T> if callers need ordering and index access. Collection<T> if only iteration and basic add/remove are needed. Map<K,V> for key-value access. This makes code more flexible and communicates the contract clearly. During code reviews, declaring ArrayList<String> result = new ArrayList<>() as a method return type is a common flag — it forces callers to depend on a specific implementation unnecessarily.
Use NavigableSet and NavigableMap when sorted range operations are needed. Declaring NavigableMap<String,Integer> map = new TreeMap<>() exposes floorKey(), ceilingKey(), headMap(), and tailMap() that would be hidden behind a plain Map<K,V> declaration. Use the widest interface for parameters, but the most specific useful interface for variables where the extra methods are needed.
Program to the hierarchy, not to the implementation. When switching from HashSet to LinkedHashSet for iteration-order consistency, only the constructor line changes if the variable is declared as Set<T>. When switching from ArrayList to CopyOnWriteArrayList for thread safety, only the constructor changes if the variable is declared as List<T>. The hierarchy exists precisely to make substitution like this safe.
Use Map.merge() and Map.computeIfAbsent() for idiomatic accumulation patterns. map.merge(key, 1, Integer::sum) accumulates a counter without a null check. map.computeIfAbsent(key, k -> new ArrayList<>()).add(value) builds a multi-value map safely. These methods are part of the Map interface in Java 8+ and should replace the verbose if (map.containsKey(key)) / else patterns that appear in older code.
Common Mistakes
Mistake 1 — Using the Implementation Type as Parameter Type
1// Couples the caller to one specific implementation — bad design
2public void processOrders(ArrayList<String> orders) { ... }
3
4// Any List implementation can be passed — better design
5public void processOrders(List<String> orders) { ... }
6
7// Even wider — if only iteration is needed
8public void processOrders(Collection<String> orders) { ... }
9// Or widest possible if only for-each is needed
10public void processOrders(Iterable<String> orders) { ... }Mistake 2 — Choosing a Collection Based on Name, Not Access Pattern
1// "It's a list of products" — but the main operation is containsKey(productId)
2List<Product> catalogue = new ArrayList<>();
3Product found = catalogue.stream()
4 .filter(p -> p.getId().equals(id))
5 .findFirst().orElse(null); // O(n) — scanning everything
6
7// The correct model: the catalogue IS a Map (id -> product)
8Map<String, Product> catalogue = new HashMap<>();
9Product found = catalogue.get(id); // O(1) — direct lookupMistake 3 — Ignoring That Map Is Not a Collection
1Map<String, Integer> scores = new HashMap<>();
2scores.put("Priya", 95); scores.put("Rohan", 88);
3
4// WRONG — Map does not extend Collection; this does not compile
5Collection<Map<String,Integer>> col = scores;
6
7// CORRECT — use the appropriate view
8Collection<Integer> values = scores.values(); // Collection<V>
9Set<String> keys = scores.keySet(); // Set<K>
10Set<Map.Entry<String,Integer>> entries = scores.entrySet(); // Set<Entry<K,V>>Mistake 4 — Using Sorted Implementation Without Providing Comparable or Comparator
1// WRONG — Product does not implement Comparable; throws ClassCastException at runtime
2Set<Product> sorted = new TreeSet<>();
3sorted.add(new Product("P001", "Laptop", 45999)); // first add works
4sorted.add(new Product("P002", "Mouse", 599)); // ClassCastException here!
5
6// CORRECT — provide a Comparator at construction
7Set<Product> sorted = new TreeSet<>(Comparator.comparingDouble(Product::getPrice));
8sorted.add(new Product("P001", "Laptop", 45999));
9sorted.add(new Product("P002", "Mouse", 599));Interview Questions
Q1. What is the Java Collections hierarchy and how is it structured?
The hierarchy has two independent branches. The Collection branch starts with java.lang.Iterable, extends to java.util.Collection, and splits into List (ordered, duplicates allowed, indexed), Set (unique elements, no duplicates), and Queue (processing order, typically FIFO). The Map branch is completely separate — Map<K,V> does not extend Collection because its key-value structure does not fit the single-element add(E) contract. Both branches live in java.util. Understanding the two-branch structure is what interviewers at both service-based and product-based companies test first.
Q2. Why does Map not extend Collection in Java?
Collection<E> models a group of individual elements — it defines add(E element) as the insertion operation. Map<K,V> models associations between keys and values — every entry is inherently two things. There is no clean way to express add(Map.Entry<K,V>) as equivalent to add(E) without breaking the Collection contract semantics. The design decision to keep Map separate keeps the abstraction clean. You access Map data through collection views: keySet() returns Set<K>, values() returns Collection<V>, and entrySet() returns Set<Map.Entry<K,V>> — all of which participate normally in the Collection hierarchy.
Q3. What is the difference between Collection and Collections in Java?
java.util.Collection (singular) is an interface — the root of the Collection hierarchy. It defines the contract that List, Set, and Queue implement. java.util.Collections (plural) is a final utility class — it cannot be instantiated and has only static methods: sort(), reverse(), shuffle(), binarySearch(), min(), max(), frequency(), unmodifiableList(), synchronizedMap(), and more. This is one of the most common interview trick questions at service-based companies.
Q4. What is the difference between Iterable and Iterator?
Iterable<E> is an interface that a collection implements — it declares one method: iterator(). Any class implementing Iterable can be used in a for-each loop. Iterator<E> is what iterator() returns — the actual cursor object with hasNext(), next(), and remove() methods that do the traversal. Iterable is the factory; Iterator is the product. The distinction matters when you need to remove elements safely during traversal — you must call iterator.remove() on the Iterator, not collection.remove() on the Collection, to avoid ConcurrentModificationException.
Q5. What does the SortedSet and NavigableSet sub-hierarchy add to Set?
SortedSet<E> extends Set and adds the guarantee that elements are always in sorted order. It adds first(), last(), headSet(toElement) (elements less than toElement), tailSet(fromElement) (elements ≥ fromElement), and subSet(from, to). NavigableSet<E> extends SortedSet and adds floor/ceiling navigation: floor(e) (largest element ≤ e), ceiling(e) (smallest element ≥ e), lower(e) (largest element strictly < e), higher(e) (smallest element strictly > e), and descendingSet(). TreeSet is the only standard implementation of NavigableSet.
Q6. What is the difference between Comparable and Comparator in the Collections hierarchy?
Comparable<T> is implemented by a class to define its natural ordering — the single default sort order built into the class via compareTo(T other). TreeSet and TreeMap use this automatically when no comparator is provided. Comparator<T> is an external ordering strategy — a separate object or lambda that defines a custom sort order without modifying the class. A class can have only one Comparable ordering but unlimited Comparator orderings. In the hierarchy, TreeSet(Comparator) and TreeMap(Comparator) constructors accept an explicit Comparator, overriding whatever natural ordering the elements may have.
FAQs
What is the root of the Java Collections hierarchy?
java.lang.Iterable<E> is the root of the Collection branch. It is in java.lang (auto-imported) and declares the iterator() method that enables for-each loops. java.util.Collection<E> extends Iterable and is the root of the actual Collections Framework. Map<K,V> has its own separate root and does not participate in the Iterable/Collection chain directly.
Can I add any collection to another collection using addAll?
Yes — Collection.addAll(Collection<? extends E> c) accepts any collection whose element type is the same or a subtype. list.addAll(set) and set.addAll(list) both compile and work correctly. Map is not a Collection, so you cannot call list.addAll(map) directly — use list.addAll(map.values()) or list.addAll(map.keySet()) to add the collection views.
What is the difference between HashSet, LinkedHashSet, and TreeSet?
All three are Set implementations — all reject duplicates. HashSet uses a hash table — fastest for add/remove/contains at O(1) average, no guaranteed iteration order. LinkedHashSet extends HashSet and maintains insertion order through a doubly-linked list alongside the hash table — still O(1) average, slightly more memory, predictable iteration. TreeSet uses a Red-Black tree — O(log n) for all operations, but elements are always iterated in sorted ascending order and range navigation methods (floor, ceiling, headSet) are available.
When should I use LinkedList instead of ArrayList?
LinkedList is worth choosing when insertions and removals at the head (front) of the list are the primary operation on a large list — addFirst() and removeFirst() are O(1) on LinkedList, O(n) on ArrayList. For a FIFO queue, LinkedList as a Queue was historically common. However, ArrayDeque is now the recommended default for both stack and queue use — it is faster than LinkedList for head/tail operations due to lower memory overhead and better cache locality. Choose LinkedList only when you genuinely need both the List and Deque interfaces on the same object simultaneously.
What is the difference between HashMap and LinkedHashMap?
HashMap stores entries in a hash table — O(1) average for all operations, but iteration order is undefined and may change after a rehash. LinkedHashMap extends HashMap and maintains an additional doubly-linked list of all entries in either insertion order (default) or access order (when constructed with new LinkedHashMap<>(capacity, loadFactor, true)). Access-order mode makes LinkedHashMap the standard foundation for LRU (Least Recently Used) caches when combined with removeEldestEntry() override. The performance cost over HashMap is minimal.
What is the purpose of the NavigableMap interface?
NavigableMap<K,V> extends SortedMap and adds fine-grained navigation operations: floorKey(k) (largest key ≤ k), ceilingKey(k) (smallest key ≥ k), lowerKey(k) (largest key strictly < k), higherKey(k) (smallest key strictly > k), pollFirstEntry() (remove and return smallest), pollLastEntry() (remove and return largest), and descendingMap() (reverse-order view). TreeMap is the standard implementation. These methods are invaluable for range-based lookups — finding the nearest price, the next available time slot, or all keys within a date range.
How does the hierarchy help with writing flexible, reusable methods?
The hierarchy lets you write methods that accept the widest contract needed, making them reusable with any conforming implementation. A method that accepts Collection<T> works with ArrayList, HashSet, TreeSet, or LinkedList. A method that accepts Iterable<T> works with all of those plus custom classes. A method that only needs Map<K,V> works with HashMap, TreeMap, and LinkedHashMap interchangeably. Programming to the interface — not the implementation — is what the entire hierarchy is designed to enable.
Summary
The Java Collections hierarchy has two independent branches: Collection (with List, Set, and Queue sub-interfaces) for single-element containers, and Map for key-value associations. Every concrete class — ArrayList, HashSet, TreeMap, ArrayDeque — implements one or more interfaces from this hierarchy, and the interface level determines what operations are available and what performance those operations provide.
The hierarchy solves one fundamental problem: writing code that works with the right contract, not the right class. A method accepting List<T> works with ArrayList and LinkedList. A method accepting Collection<T> works with all lists, sets, and queues. A method accepting Map<K,V> works with every map implementation. This substitutability — guaranteed by the hierarchy — is what makes Java collections reusable, testable, and maintainable across large codebases.
For interviews: draw the hierarchy from memory, explain why Map does not extend Collection, know the difference between Collection and Collections, and understand what each interface layer adds beyond its parent.
What to Read Next
| Topic | Link |
|---|---|
| How ArrayList implements the List interface with a resizable backing array | Java ArrayList → |
| How HashMap implements the Map interface with hashing, buckets, and O(1) lookup | Java HashMap → |
| How the Iterator interface connects every collection to safe traversal | Java Iterator → |
| How Generics give every interface in the hierarchy compile-time type safety | Java Generics → |
| How Java Streams process any collection type with a unified functional API | Java Streams API → |