Introduction
Why Collections Came into Picture:
βοΈIn Java, arrays have a fixed size and lack built-in methods for easy manipulation like adding, removing, or searching elements. To overcome these limitations, the Collection Framework was introduced, providing dynamic data structures with rich utility methods. For a detailed understanding of arrays in Java, you can check Java Arrays Tutorial
The Java Collection Framework (JCF) is a unified architecture for storing and manipulating groups of objects. It provides ready-to-use data structures (like List, Set, Map, Queue) and algorithms (like sorting, searching, iteration).
Instead of writing complex data structures manually, Java developers can use these pre-built and optimized collections.
π The framework is part of java.util package and was introduced in Java 2 (JDK 1.2), but has been enhanced with each release up to the latest Java 25.
Why Use Collection Framework?
- Reduces development effort β ready-to-use data structures.
- Improves performance β optimized implementations.
- Increases code quality β reusable, consistent APIs.
- Type safety with Generics (introduced in Java 5).
- Concurrent utilities (from Java 5 onwards, updated till Java 25).
Collection Framework Hierarchy
At the top level, we have two main root interfaces:
- Collection Interface (
java.util.Collection)- Extended by List, Set, and Queue.
- Map Interface (
java.util.Map)- A separate hierarchy for key-value pairs.
Hierarchy Overview:
Iterable
|
+--------------+-------------------------+
| |
+-----Collection---------------+ Map
/ | | |
/ | | |
List Set----+ Queue SortedMap
/ | \ | |
+--------+----------+ | \ | |
| | | SortedSet HashSet Deque NavigableMap
ArrayList Vector LinkedList | |
NavigableSet LinkedHashSet
|
TreeSet
Key Interfaces and Classes
At the heart of the Java Collections Framework are a set of key interfaces β Collection, List, Set, Queue, Deque, and Map. These interfaces establish the contracts that different collection classes must follow, providing guidelines for how data can be stored, accessed, and manipulated.
- List Interface
- Set Interface
- Queue Interface
- Deque Interface
- Map Interface
1. List Interface
Represents an ordered collection that allows duplicate elements and provides positional access. Lists include dynamic arrays, linked structures, and legacy classes designed for sequential storage.
- Index-based access.
Popular Implementations:
ArrayListLinkedListVectorStackCopyOnWriteArrayList
β Example:
import java.util.*;
public class ListExample {
public static void main(String[] args) {
List<String> fruits = new ArrayList<>();
fruits.add("Apple");
fruits.add("Banana");
fruits.add("Mango");
fruits.add("Apple"); // allows duplicates
System.out.println("Fruits List: " + fruits);
}
}
2. Set Interface
Represents a collection that does not allow duplicate elements. Sets are used to model mathematical sets and may or may not preserve insertion order, depending on the implementation.
Popular Implementations:
HashSetLinkedHashSetTreeSetCopyOnWriteArraySet
β Example:
import java.util.*;
public class SetExample {
public static void main(String[] args) {
Set<String> names = new HashSet<>();
names.add("Simone");
names.add("Jeremy");
names.add("Simone"); // ignored
System.out.println("Names: " + names);
}
}
3. Queue Interface
Represents a collection designed for holding elements prior to processing. Queues typically follow FIFO (First-In-First-Out) order, but priority-based or custom orderings are also possible.
Popular Implementations:
LinkedListPriorityQueueArrayDequeConcurrentLinkedQueueLinkedBlockingQueue
β Example:
import java.util.*;
public class QueueExample {
public static void main(String[] args) {
Queue<Integer> queue = new LinkedList<>();
queue.add(10);
queue.add(20);
queue.add(30);
System.out.println("Queue: " + queue);
System.out.println("Removed: " + queue.poll()); // removes first
System.out.println("After removal: " + queue);
}
}
4. Deque Interface
A double-ended queue that supports element insertion and removal at both ends. Deques can function as stacks (LIFO) or queues (FIFO), providing flexible access patterns.
Popular Implementations:
ArrayDeque.ConcurrentLinkedDequeLinkedBlockingDequeLinkedList
β Example:
import java.util.*;
public class DequeExample {
public static void main(String[] args) {
Deque<String> dq = new ArrayDeque<>();
dq.addFirst("Start");
dq.addLast("End");
System.out.println(dq);
}
}
5. Map Interface
Represents a collection of keyβvalue pairs, where keys are unique and each key maps to exactly one value. Maps are not true collections but provide a way to store and retrieve data based on unique identifiers.
- Keys are unique, values can be duplicate.
Popular Implementations:
HashMapLinkedHashMapTreeMapHashtableConcurrentHashMap(thread-safe)
β Example:
import java.util.*;
public class MapExample {
public static void main(String[] args) {
Map<Integer, String> students = new HashMap<>();
students.put(1, "Alice");
students.put(2, "Bob");
students.put(3, "Charlie");
System.out.println("Students: " + students);
System.out.println("Student with ID 2: " + students.get(2));
}
}
6.Utility Class: Collections
The Collections class (part of java.util package) is a utility class that provides static methods to operate on or return collections.
It is different from the Collections Framework itself. The Collections Framework provides data structures (List, Set, Map, etc.), while the Collections class provides algorithms and helper methods to work with those data structures.
Key Features of Collections Class
- Algorithms β Sorting, Searching, Shuffling, Reversing, Rotating, etc.
- Synchronization Wrappers β Convert non-thread-safe collections into thread-safe ones.
- Read-only Wrappers β Create immutable collections.
- Utility Methods β Frequency count, finding min/max, filling collections, etc.
Method :
| Method | Description | Example |
|---|---|---|
sort(List<T> list) | Sorts the list in ascending order. | Collections.sort(list); |
reverse(List<?> list) | Reverses the list order. | Collections.reverse(list); |
min(Collection<? extends T> coll) | Returns minimum element. | Collections.min(list); |
max(Collection<? extends T> coll) | Returns maximum element. | Collections.max(list); |
frequency(Collection<?> c, Object o) | Counts frequency of element. | Collections.frequency(list, "Java"); |
β Example:
Example 1: Sorting and Reversing
import java.util.*;
public class CollectionsExample1 {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Aryan");
names.add("Ashish");
names.add("bhasker");
names.add("Devid");
// Sorting (Ascending Order)
Collections.sort(names);
System.out.println("Sorted List: " + names);
// Reversing
Collections.reverse(names);
System.out.println("Reversed List: " + names);
// Shuffling
Collections.shuffle(names);
System.out.println("Shuffled List: " + names);
}
}
Output (may vary due to shuffle):
Sorted List: [Aryan, Ashish, Bhasker, Devid]
Reversed List: [Devid, Bhasker, Ashish, Aryan]
Shuffled List: [Ashish, Devid, Bhasker, Aryan] // shuffle can change each time
Example 2: Finding Min, Max, and Frequency
import java.util.*;
public class CollectionsExample2 {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(10, 20, 30, 10, 50, 10);
int min = Collections.min(numbers);
int max = Collections.max(numbers);
int freq = Collections.frequency(numbers, 10);
System.out.println("Minimum: " + min);
System.out.println("Maximum: " + max);
System.out.println("Frequency of 10: " + freq);
}
}
Output:
Minimum: 10
Maximum: 50
Frequency of 10: 3
Example 3: Creating Read-only and Synchronized Collections
import java.util.*;
public class CollectionsExample3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("Java");
list.add("Python");
list.add("C++");
// Unmodifiable (Read-only) List
List<String> unmodifiableList = Collections.unmodifiableList(list);
System.out.println("Unmodifiable List: " + unmodifiableList);
// unmodifiableList.add("Go"); // Throws UnsupportedOperationException
// Synchronized List (Thread-safe wrapper)
List<String> syncList = Collections.synchronizedList(list);
System.out.println("Synchronized List: " + syncList);
}
}