A Beginner's Guide to Java Collections

Sambhav RanaSambhav Rana
8 min read

Hey everyone! If you're starting your journey with Java, you've probably heard about the Java Collections Framework (JCF). It sounds complex, but it's one of the most powerful and essential parts of Java. Its job is to give you a bunch of tools to store and manage groups of objects efficiently.

But what's the best way to understand it? Let's forget the complicated jargon for a minute and think about something we all know: school.

Imagine you're the principal of a school, and you need to manage all your students, classes, and records. You wouldn't use the same method for everything, right?

  • A List is like your daily attendance register. It's an ordered list of students. You can have two students named 'Rahul' (duplicates are allowed), and you know who is at roll number 1, 5, or 20 (it's ordered by an index).

  • A Queue is like the line for the school canteen. The student who gets in line first is the first one to get their food (First-In, First-Out).

  • A Set is like the list of unique house teams (e.g., Red, Blue, Green, Yellow). You can't have 'Blue' house listed twice. It’s a collection of unique items.

  • A Map is like a student's report card. It connects a unique key (the subject, like 'Maths') to a value (the marks, like 95).

Throughout this guide, we'll keep coming back to this simple analogy to understand how each part of the Java Collections Framework works.

1. The List Interface: The Attendance Register πŸ“‹

A List is an ordered collection that allows duplicate elements. It's your go-to when you need to control the position of elements and access them by their number in the list (their index).

ArrayList

An ArrayList is like a standard student roster written on a sheet of paper.

  • Theory: It uses a dynamic array in the background. This makes it super fast to find a student if you know their roll number (getting an element by index), which takes O(1) time. However, if a new student joins in the middle, you have to erase and shift all the students below them, which is slow (O(n)).

  • Best For: When you need to read data frequently and don't have too many additions or removals.

Java

// Create an ArrayList
List<Integer> al = new ArrayList<>();

// Add elements
al.add(4);    // Appends 4 to the end -> [4]
al.add(40);   // Appends 40 to the end -> [4, 40]
al.add(1, 33); // Inserts 33 at index 1 -> [4, 33, 40]

System.out.println(al); // Output: [4, 33, 40]

LinkedList

A LinkedList is like a chain of students holding hands.

  • Theory: Each student (or "node") only knows who is in front of them and who is behind them. This makes it very easy for a new student to join or leave the middle of the chainβ€”they just need to grab or let go of hands. This is very fast (O(1)). But, to find the 10th student, you have to start from the beginning and count one by one, which is slow (O(n)).

  • Best For: When you have a lot of additions and removals in your list.

Java

// Create a LinkedList
List<Integer> ll = new LinkedList<>();

// Operations are the same as ArrayList
ll.add(4);
ll.add(40);
ll.add(2, 33); // Inserts 33 at index 2 -> [4, 40, 33]

System.out.println(ll); // Output: [4, 40, 33]

Stack

A Stack is like a stack of notebooks on a desk. You can only take the top one.

  • Theory: It follows a Last-In, First-Out (LIFO) principle. The last book you put on the stack is the first one you remove. The main operations are push() (add to top), pop() (remove from top), and peek() (look at the top item).

  • Modern Practice: While Stack works, it's an older class. For stack operations today, it's better to use an ArrayDeque.

Java

Stack<Integer> s = new Stack<>();
s.push(1);
s.push(2); // 2 is now at the top

System.out.println("Stack: " + s); // Output: [1, 2]
System.out.println("Top of Stack: " + s.peek()); // Output: 2

2. The Queue Interface: The Canteen Line πŸšΆβ€β™‚οΈπŸšΆβ€β™€οΈπŸšΆβ€β™‚οΈπŸšΆβ€β™‚οΈπŸšΆβ€β™€οΈ

A Queue is used to hold elements before you process them. Usually, this is done in a First-In, First-Out (FIFO) manner, just like a line.

PriorityQueue

This is not your normal queue; it's a VIP queue.

  • Theory: Instead of being strictly FIFO, elements are ordered based on priority. By default, the smallest element gets the highest priority (this is called a min-heap). Imagine a younger student being allowed to get their food first. You can change this to a max-heap (where the largest element gets priority) if you need to.

  • Performance: Adding and removing elements takes O(logn) time.

Java

// Min-Heap (smallest element has priority)
Queue<Integer> pq = new PriorityQueue<>();
pq.offer(10);
pq.offer(40);
pq.offer(5); // 5 is the highest priority
System.out.println("Min-Heap: " + pq); // Output: [5, 40, 10]
System.out.println("Head of queue is: " + pq.peek()); // Output: 5

// Max-Heap (largest element has priority)
Queue<Integer> pqMax = new PriorityQueue<>(Comparator.reverseOrder());
pqMax.offer(10);
pqMax.offer(40);
pqMax.offer(5); // 40 is the highest priority
System.out.println("Max-Heap: " + pqMax); // Output: [40, 10, 5]
System.out.println("Head of queue is: " + pqMax.peek()); // Output: 40

ArrayDeque

This is a "double-ended" queue, like a line where people can join or leave from both the front and the back.

  • Theory: ArrayDeque (Array Double Ended Queue) is super versatile. Because you can add or remove from both ends, it can act as a normal queue (FIFO) or a stack (LIFO). It's faster and more efficient than the older Stack and LinkedList classes for these jobs.

  • Performance: Adding or removing from either end takes O(1) time on average.

Java

ArrayDeque<Integer> ad = new ArrayDeque<>();
ad.offer(20);       // Adds to the end -> [20]
ad.offerFirst(10);  // Adds to the front -> [10, 20]
ad.offerLast(30);   // Adds to the end -> [10, 20, 30]

System.out.println(ad); // Output: [10, 20, 30]

ad.pollFirst();      // Removes from front -> [20, 30]
System.out.println(ad); // Output: [20, 30]

3. The Set Interface: The Unique House Teams πŸ†

A Set is a collection that holds no duplicate elements. It’s perfect when you just need to store unique items and don't care about their order.

HashSet

Stores items in a hash table. Think of it as throwing all the unique house names into a big, unorganized box.

  • Theory: It's extremely fast because it uses a technique called hashing to store items. The downside? There are no guarantees about the order of the elements.

  • Performance: O(1) average time for adding, removing, and checking if an item exists.

LinkedHashSet

This is like HashSet, but it also remembers the order you added the items in.

  • Theory: It combines the speed of a hash table with the predictability of a linked list. The elements will always be in insertion order.

  • Performance: Almost as fast as HashSet.

TreeSet

This keeps the elements sorted at all times.

  • Theory: It stores elements in a special tree structure (a Red-Black Tree) that automatically keeps them in sorted order.

  • Performance: A bit slower than HashSet, with O(logn) time for adding, removing, and checking for items.

Java

Set<Integer> hs = new HashSet<>();          // Unordered
hs.add(30); hs.add(10); hs.add(50); hs.add(20);
System.out.println("HashSet (Unordered): " + hs);

Set<Integer> lhs = new LinkedHashSet<>();   // Insertion Order
lhs.add(30); lhs.add(10); lhs.add(50); lhs.add(20);
System.out.println("LinkedHashSet (Insertion Order): " + lhs);

Set<Integer> ts = new TreeSet<>();          // Sorted Order
ts.add(30); ts.add(10); ts.add(50); ts.add(20);
System.out.println("TreeSet (Sorted Order): " + ts);

4. The Map Interface: The Report Card πŸ“

A Map is an object that stores key-value pairs. Just like a report card maps a subject (key) to marks (value), a Map links a unique key to a specific value. Keys must be unique.

HashMap

The fastest type of map, but it makes no promises about order.

  • Theory: It uses the key's hash code to store the key-value pair. It's unordered. If you add a new value with an existing key, the old value gets replaced.

  • Performance: O(1) average time to get or put a value.

LinkedHashMap

This is a HashMap that also remembers the order in which you inserted the items.

  • Theory: It maintains insertion order, so when you iterate through it, the elements appear in the same sequence you added them.

  • Performance: Nearly as fast as HashMap.

TreeMap

This map keeps its entries sorted by the key.

  • Theory: It stores the key-value pairs in a tree structure, ensuring the map is always sorted by its keys.

  • Performance: O(logn) time for getting, putting, and removing entries.

Java

// Unordered
Map<Integer, String> hm = new HashMap<>();
hm.put(1, "Dad");
hm.put(55, "Son");
hm.put(2, "Mother");
System.out.println("HashMap (Unordered): " + hm);

// Insertion Order
Map<Integer, String> lhm = new LinkedHashMap<>();
lhm.put(1, "Dad");
lhm.put(55, "Son");
lhm.put(2, "Mother");
System.out.println("LinkedHashMap (Insertion Order): " + lhm);

// Sorted by key
Map<Integer, String> tm = new TreeMap<>();
tm.put(1, "Dad");
tm.put(55, "Son");
tm.put(2, "Mother");
System.out.println("TreeMap (Sorted by Key): " + tm);

Final Thoughts

And that's it! The Java Collections Framework is all about choosing the right tool for the job. By remembering our school analogy, you can easily decide which collection you need:

  • Need an ordered list with duplicates? Use a List.

  • Need a First-In, First-Out queue? Use a Queue.

  • Need to store unique items? Use a Set.

  • Need to link unique keys to values? Use a Map.

7
Subscribe to my newsletter

Read articles from Sambhav Rana directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Sambhav Rana
Sambhav Rana