Core Java · Lesson 12
Collections Framework
Collections: Organizing and Managing Data
The Java Collections Framework is a unified architecture for storing, retrieving, and manipulating groups of objects. Instead of using arrays (fixed size) or rolling your own data structures, you use built-in collections optimized for different purposes. Collections include Lists (ordered, duplicates allowed), Sets (unique elements), Maps (key-value pairs), and Queues (ordered for retrieval). Mastering collections is essential — you'll use them constantly.
Lists: Ordered Collections with Duplicates
A List maintains insertion order and allows duplicates. ArrayList is a resizable array (fast random access); LinkedList is a doubly-linked list (fast insertion/deletion at ends). Use ArrayList for most cases; use LinkedList if you frequently insert/delete at the beginning.
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
// ArrayList: fast random access
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Cherry");
fruits.add("Apple"); // Duplicates allowed
System.out.println("ArrayList: " + fruits);
System.out.println("First fruit: " + fruits.get(0));
System.out.println("Contains Banana: " + fruits.contains("Banana"));
// LinkedList: efficient insertion/deletion at ends
List<Integer> numbers = new LinkedList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);
numbers.addFirst(5); // Insert at beginning
numbers.addLast(40); // Insert at end
System.out.println("LinkedList: " + numbers);
// Iteration
System.out.println("Iterating:");
for (String fruit : fruits) {
System.out.println(" " + fruit);
}
// Remove
fruits.remove("Apple"); // Removes first occurrence
System.out.println("After removing Apple: " + fruits);
}
}
Sets: Unique Elements Only
A Set contains only unique elements (no duplicates). HashSet offers fast lookups (unordered); TreeSet keeps elements sorted; LinkedHashSet maintains insertion order. Use HashSet for general purposes, TreeSet when you need sorting.
import java.util.HashSet;
import java.util.TreeSet;
import java.util.LinkedHashSet;
import java.util.Set;
public class SetExample {
public static void main(String[] args) {
// HashSet: fast, unordered
Set<String> uniqueFruits = new HashSet<>();
uniqueFruits.add("Apple");
uniqueFruits.add("Banana");
uniqueFruits.add("Cherry");
uniqueFruits.add("Apple"); // Duplicate: ignored
System.out.println("HashSet: " + uniqueFruits); // Order unpredictable
System.out.println("Size: " + uniqueFruits.size()); // 3
// TreeSet: sorted
Set<Integer> sortedNumbers = new TreeSet<>();
sortedNumbers.add(30);
sortedNumbers.add(10);
sortedNumbers.add(20);
sortedNumbers.add(10); // Duplicate: ignored
System.out.println("TreeSet: " + sortedNumbers); // [10, 20, 30]
// LinkedHashSet: insertion order maintained
Set<String> linkedFruits = new LinkedHashSet<>();
linkedFruits.add("Mango");
linkedFruits.add("Grape");
linkedFruits.add("Pear");
System.out.println("LinkedHashSet: " + linkedFruits);
}
}
Maps: Key-Value Associations
A Map stores key-value pairs. HashMap is fast and unordered; TreeMap keeps keys sorted; LinkedHashMap maintains insertion order. Use HashMap for lookups; use TreeMap if you need keys in sorted order.
import java.util.HashMap;
import java.util.TreeMap;
import java.util.Map;
public class MapExample {
public static void main(String[] args) {
// HashMap: fast lookups, unordered
Map<String, Integer> studentScores = new HashMap<>();
studentScores.put("Alice", 95);
studentScores.put("Bob", 87);
studentScores.put("Charlie", 92);
System.out.println("HashMap: " + studentScores);
System.out.println("Alice's score: " + studentScores.get("Alice"));
System.out.println("Contains Bob: " + studentScores.containsKey("Bob"));
// TreeMap: sorted by keys
Map<String, String> sortedCountries = new TreeMap<>();
sortedCountries.put("India", "New Delhi");
sortedCountries.put("Brazil", "Brasília");
sortedCountries.put("Canada", "Ottawa");
System.out.println("TreeMap: " + sortedCountries); // Sorted by key
// Iteration
System.out.println("All scores:");
for (String name : studentScores.keySet()) {
System.out.println(" " + name + ": " + studentScores.get(name));
}
// Iteration with entrySet (more efficient)
System.out.println("Using entrySet:");
for (Map.Entry<String, Integer> entry : studentScores.entrySet()) {
System.out.println(" " + entry.getKey() + ": " + entry.getValue());
}
// Remove
studentScores.remove("Bob");
System.out.println("After removing Bob: " + studentScores);
}
}
Queues and Deques: Sequential Access
A Queue maintains FIFO (First-In-First-Out) order — elements are added at the end and removed from the front. A Deque (double-ended queue) allows insertion/removal at both ends. Use queues for tasks needing sequential processing; use deques for flexible access patterns.
import java.util.Queue;
import java.util.Deque;
import java.util.LinkedList;
public class QueueExample {
public static void main(String[] args) {
// Queue: FIFO
Queue<String> taskQueue = new LinkedList<>();
taskQueue.offer("Task 1");
taskQueue.offer("Task 2");
taskQueue.offer("Task 3");
System.out.println("Queue: " + taskQueue);
System.out.println("Next task: " + taskQueue.poll()); // Remove from front
System.out.println("Peek: " + taskQueue.peek()); // View front without removing
// Deque: double-ended
Deque<Integer> deque = new LinkedList<>();
deque.addFirst(1);
deque.addLast(2);
deque.addFirst(0);
System.out.println("Deque: " + deque);
System.out.println("Remove first: " + deque.removeFirst());
System.out.println("Remove last: " + deque.removeLast());
}
}
Common Collection Algorithms
Java provides utility methods for common operations: sorting, searching, reversing, shuffling. The Collections class (note: singular "Collection" for interface, plural for utility) provides these static methods.
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
public class CollectionAlgorithms {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(5);
numbers.add(2);
numbers.add(8);
numbers.add(1);
numbers.add(9);
System.out.println("Original: " + numbers);
// Sort
Collections.sort(numbers);
System.out.println("Sorted: " + numbers);
// Reverse
Collections.reverse(numbers);
System.out.println("Reversed: " + numbers);
// Shuffle
Collections.shuffle(numbers);
System.out.println("Shuffled: " + numbers);
// Binary search (requires sorted list)
Collections.sort(numbers);
int index = Collections.binarySearch(numbers, 8);
System.out.println("Index of 8: " + index);
// Max and min
System.out.println("Max: " + Collections.max(numbers));
System.out.println("Min: " + Collections.min(numbers));
// Frequency
List<String> words = new ArrayList<>();
words.add("java");
words.add("python");
words.add("java");
System.out.println("'java' appears: " + Collections.frequency(words, "java") + " times");
}
}
| Collection | Ordered | Duplicates | Null Values | Performance |
|---|---|---|---|---|
| ArrayList | ✓ | ✓ | ✓ | Fast access, O(1) |
| LinkedList | ✓ | ✓ | ✓ | Fast insert/delete at ends |
| HashSet | ✗ | ✗ | ✓ | Fast lookup, O(1) |
| TreeSet | ✓ | ✗ | ✗ | Sorted, O(log n) |
| HashMap | ✗ | ✗ | One null key | Fast lookup, O(1) |
| TreeMap | ✓ | ✗ | ✗ | Sorted keys, O(log n) |
Common Mistake: Modifying a collection while iterating over it. If you add or remove elements during iteration, you'll get a
ConcurrentModificationException. Always use an iterator'sremove()method, or create a copy before modifying. Also, watch for null values —TreeSetandTreeMapdon't accept nulls because they use comparison for ordering.
Best Practices for Collections
- Use ArrayList for most cases; it's the default choice
- Use HashSet/HashMap for fast lookups when order doesn't matter
- Use TreeSet/TreeMap when you need elements sorted
- Choose the right collection type upfront; switching later is refactoring
- Use generics (
List<String>) to catch type errors at compile time - Iterate with for-each loops when you don't need the iterator
- Use
entrySet()notkeySet()when iterating over Maps - Consider immutable collections (Collections.unmodifiableList) for defensive copying
Real-World Example: E-Commerce Shopping System
A shopping cart uses an ArrayList to maintain items in order; a HashMap maps product IDs to inventory; a TreeMap sorts orders by date; a HashSet tracks unique customer IDs. When displaying the cart, it iterates with a for-each loop. When processing checkout, it validates inventory using the HashMap, then creates an immutable order record. Searching for a product uses binary search on a sorted inventory list. By choosing the right collection for each responsibility, the system is both efficient and maintainable.
Quiz Check