Java Tutorial
🔍

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.

Java
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.

Java
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.

Java
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().

Java
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.

Java
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.

Java
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.

Java
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}
Java
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

InterfaceImplementationget / lookupaddremovecontainsOrder
ListArrayListO(1) by indexO(1) amortO(n) shiftO(n) scanInsertion
ListLinkedListO(n) traverseO(1) endsO(1) endsO(n) scanInsertion
SetHashSetO(1) avgO(1) avgO(1) avgNone
SetLinkedHashSetO(1) avgO(1) avgO(1) avgInsertion
SetTreeSetO(log n)O(log n)O(log n)Sorted
QueueArrayDequeO(1) endsO(1) amortO(1) endsO(n)FIFO/LIFO
QueuePriorityQueueO(1) min peekO(log n)O(log n)O(n)Priority
MapHashMapO(1) avgO(1) avgO(1) avgO(1) avg keyNone
MapLinkedHashMapO(1) avgO(1) avgO(1) avgO(1) avg keyInsertion
MapTreeMapO(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

Java
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

Java
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 lookup

Mistake 3 — Ignoring That Map Is Not a Collection

Java
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

Java
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

TopicLink
How ArrayList implements the List interface with a resizable backing arrayJava ArrayList →
How HashMap implements the Map interface with hashing, buckets, and O(1) lookupJava HashMap →
How the Iterator interface connects every collection to safe traversalJava Iterator →
How Generics give every interface in the hierarchy compile-time type safetyJava Generics →
How Java Streams process any collection type with a unified functional APIJava Streams API →
Java Collections Hierarchy | DevStackFlow