Collections Framework in Java
The Java Collections Framework (JCF) is one of the most important concepts in Java programming.
Almost every real-world application needs to manage multiple pieces of data — such as employee records, product lists, usernames, orders, messages, or tasks.
Instead of creating separate variables or building data structures from scratch, Java provides a well-designed framework containing interfaces, classes, and utility methods to store and manipulate groups of objects efficiently.
The image below represents the basic hierarchy of some important interfaces and implementations in the Java Collections Framework:
Iterable → Collection → List / Set / Queue → Implementation Classes
Let’s understand the Collections Framework from scratch.
What is the Java Collections Framework?
The Java Collections Framework is a unified architecture that provides:
- Interfaces for defining different types of collections
- Classes that implement those interfaces
- Methods for adding, removing, searching, and sorting data
- Algorithms for performing common operations
In simple words:
The Collections Framework provides ready-made data structures to store and manage groups of objects.
For example, instead of creating many variables:
String student1 = "Ali";
String student2 = "John";
String student3 = "Sara";
String student4 = "David";We can use a collection:
ArrayList<String> students = new ArrayList<>();
students.add("Ali");
students.add("John");
students.add("Sara");
students.add("David");Now all student objects are managed inside one collection.
Why Do We Need Collections?
Before Collections, programmers often used arrays to store multiple values.
String[] names = new String[5];Arrays are useful, but they have some limitations.
Limitations of Arrays
- Fixed size
- Difficult insertion and deletion
- Limited built-in methods
- Cannot easily change size dynamically
For example:
int[] numbers = new int[5];The size is fixed at 5. If you need more space, you need to create another array.
Collections solve many of these problems.
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(10);
numbers.add(20);
numbers.add(30);The ArrayList can grow dynamically as elements are added.
Understanding the Hierarchy
The basic structure shown in the diagram is:
Iterable
|
Collection
|
--------------------------------
| | |
List Set Queue
| | |
ArrayList HashSet PriorityQueue
LinkedList TreeSet
LinkedHashSetThe diagram represents an important concept:
As we move downward, we go from general interfaces to more specific interfaces and finally to concrete implementation classes.
Let’s understand each level.
1. Iterable Interface
At the top of the hierarchy is the Iterable interface.
The main purpose of Iterable is to allow an object to be traversed or looped through.
For example:
for (String name : names) {
System.out.println(name);
}This enhanced for-loop works because collections implement the Iterable interface.
The Iterable interface provides the iterator() method.
Conceptually:
Iterator<T> iterator();An iterator allows us to access elements one by one.
Example:
ArrayList<String> names = new ArrayList<>();names.add("Ali");
names.add("John");
names.add("Sara");Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}Output:
Ali
John
SaraWhy is Iterable important?
Because it provides a standard way to traverse different collections.
Whether you are working with:
- ArrayList
- LinkedList
- HashSet
- TreeSet
You can iterate through their elements.
2. Collection Interface
The next major interface is Collection.
The Collection interface represents a group of objects.
It provides common operations that most collections need.
Some commonly used methods are:
add()
remove()
size()
isEmpty()
contains()
clear()For example:
Collection<String> names = new ArrayList<>();
names.add("Ali");
names.add("John");
System.out.println(names.size());Output:
2The Collection interface acts as the parent for major collection types such as:
- List
- Set
- Queue
Each of these has different rules and behavior.
Collection vs Collections
Beginners often confuse these two terms.
Collection
Collection is an interface.
Collection<String> names;It represents a group of objects.
Collections
Collections is a utility class containing useful static methods.
For example:
Collections.sort(list);
Collections.reverse(list);
Collections.max(list);
Collections.min(list);So remember:
Collection = Interface
Collections = Utility class
3. List Interface
A List is an ordered collection of elements.
The most important characteristics of a List are:
- Maintains insertion order
- Allows duplicate elements
- Supports index-based access
- Can contain multiple
nullvalues depending on implementation
Example:
List<String> names = new ArrayList<>();names.add("Ali");
names.add("John");
names.add("Ali");
names.add("Sara");
System.out.println(names);Output:
[Ali, John, Ali, Sara]Notice that "Ali" appears twice.
This is allowed because Lists allow duplicates.
Common List Implementations
The diagram shows two major implementations:
- ArrayList
- LinkedList
Let’s understand them.
ArrayList
ArrayList is one of the most commonly used collection classes in Java.
It uses a dynamic array internally.
Example:
ArrayList<String> students = new ArrayList<>();students.add("Ali");
students.add("John");
students.add("Sara");
System.out.println(students);Important characteristics
- Maintains insertion order
- Allows duplicates
- Allows index-based access
- Dynamically grows
- Fast for accessing elements
- Slower for frequent insertion or deletion in the middle
Example of accessing by index:
System.out.println(students.get(1));Output:
JohnWhen should you use ArrayList?
Use ArrayList when:
- You frequently access elements by index
- You mostly add elements at the end
- You need an ordered collection
- Duplicates are allowed
LinkedList
LinkedList is another implementation of the List interface.
Unlike ArrayList, it does not store elements in a simple continuous array structure.
Conceptually, elements are connected as nodes.
[Ali] → [John] → [Sara] → [David]Each node maintains connections to neighboring nodes.
Example:
LinkedList<String> names = new LinkedList<>();
names.add("Ali");
names.add("John");
names.add("Sara");Characteristics
- Maintains insertion order
- Allows duplicates
- Can grow dynamically
- Efficient for insertion and deletion at suitable positions
- Slower for random index access compared to ArrayList
When should you use LinkedList?
Use LinkedList when your program performs frequent:
- Insertions
- Deletions
- Additions at the beginning or end
ArrayList vs LinkedList
A simple rule:
Use ArrayList for frequent reading/accessing.
Consider LinkedList when your workload benefits from frequent structural insertions/removals.
4. Set Interface
A Set is a collection that does not allow duplicate elements.
Example:
Set<String> names = new HashSet<>();names.add("Ali");
names.add("John");
names.add("Ali");
names.add("Sara");
System.out.println(names);Even though "Ali" was added twice, it will only appear once.
Main characteristics of Set
- Does not allow duplicates
- Does not support index-based access
- Different implementations handle ordering differently
The diagram includes:
- HashSet
- TreeSet
- LinkedHashSet
Let’s explore each one.
HashSet
HashSet stores unique elements.
Example:
HashSet<String> names = new HashSet<>();names.add("Ali");
names.add("John");
names.add("Sara");
names.add("Ali");
System.out.println(names);Characteristics
- No duplicate elements
- Does not guarantee insertion order
- Generally provides fast add, remove, and search operations
- Uses hashing internally
Important:
HashSet ≠ Sorted collection
HashSet ≠ Insertion ordered collectionThe order of elements should not be relied upon.
When to use HashSet?
Use HashSet when:
- You need unique elements
- Order is not important
- Fast lookup is important
For example, storing unique email addresses:
Set<String> emails = new HashSet<>();LinkedHashSet
LinkedHashSet is similar to HashSet, but it maintains insertion order.
Example:
LinkedHashSet<String> names = new LinkedHashSet<>();names.add("Ali");
names.add("John");
names.add("Sara");
System.out.println(names);Output:
[Ali, John, Sara]Characteristics
- No duplicates
- Maintains insertion order
- Slightly more overhead than HashSet
When to use LinkedHashSet?
Use it when:
You need unique elements and want to remember the order in which they were inserted.
TreeSet
TreeSet stores unique elements in sorted order.
Get Articles Media’s stories in your inbox
Join Medium for free to get updates from this writer.
Example:
TreeSet<Integer> numbers = new TreeSet<>();numbers.add(50);
numbers.add(10);
numbers.add(30);
numbers.add(20);
System.out.println(numbers);Output:
[10, 20, 30, 50]Characteristics
- No duplicates
- Automatically sorts elements
- Does not maintain insertion order
- Typically slower than HashSet for basic operations because ordering must be maintained
When should you use TreeSet?
Use TreeSet when:
- Duplicate values are not allowed
- You need sorted data
For example:
Set<Integer> scores = new TreeSet<>();HashSet vs LinkedHashSet vs TreeSet
A useful way to remember:
HashSet → Unique elements
LinkedHashSet → Unique + insertion order
TreeSet → Unique + sorted
5. Queue Interface
A Queue is designed for processing elements in a particular order.
The most common principle associated with queues is:
FIFO — First In, First Out
Imagine a line at a ticket counter.
Person 1 → Person 2 → Person 3The person who enters first gets served first.
Example:
Queue<String> queue = new LinkedList<>();queue.add("Ali");
queue.add("John");
queue.add("Sara");
System.out.println(queue.poll());Output:
AliThe first element is removed first.
Important Queue Methods
add()
Adds an element.
queue.add("Task");offer()
Adds an element and is designed for capacity-restricted queues to report failure without throwing an exception.
queue.offer("Task");poll()
Retrieves and removes the head of the queue.
queue.poll();peek()
Retrieves the head without removing it.
queue.peek();PriorityQueue
PriorityQueue is a special type of Queue.
Unlike a normal FIFO queue, elements are processed according to their priority.
Example:
PriorityQueue<Integer> numbers = new PriorityQueue<>();numbers.add(30);
numbers.add(10);
numbers.add(20);
System.out.println(numbers.poll());Output:
10The highest-priority element according to the queue’s ordering is processed first. With Java’s default ordering for numbers, the smallest value has the highest priority.
Example use cases
Priority queues are useful for:
- Task scheduling
- Hospital emergency systems
- CPU scheduling
- Graph algorithms
- Job processing systems
Important Note: Map Interface
Although Map is part of the broader Java Collections Framework, it is not a child of the Collection interface.
This is because a Map stores key-value pairs rather than individual elements.
Conceptually:
Key → ValueExample:
Map<Integer, String> students = new HashMap<>();students.put(101, "Ali");
students.put(102, "John");
students.put(103, "Sara");Here:
101 → Ali
102 → John
103 → SaraYou can retrieve a value using its key:
System.out.println(students.get(102));Output:
JohnCommon Map Implementations
Some important implementations include:
- HashMap
- LinkedHashMap
- TreeMap
HashMap
- Stores key-value pairs
- Keys must be unique
- Does not guarantee insertion order
LinkedHashMap
- Stores key-value pairs
- Maintains insertion order
TreeMap
- Stores key-value pairs
- Sorts entries based on keys
Understanding Interfaces and Classes
One of the most important concepts in the Collections Framework is the difference between an interface and an implementation class.
For example:
List<String> names = new ArrayList<>();Here:
List → Interface
ArrayList → Implementation ClassWhy is this useful?
Because you can change the implementation without significantly changing the rest of your code.
For example:
List<String> names = new ArrayList<>();Later:
List<String> names = new LinkedList<>();The reference type remains List.
This follows the programming principle:
Program to interfaces, not implementations.
Common Methods in Collections
Most collection classes provide useful methods.
add()
Adds an element.
list.add("Java");remove()
Removes an element.
list.remove("Java");size()
Returns the number of elements.
System.out.println(list.size());contains()
Checks whether an element exists.
System.out.println(list.contains("Java"));isEmpty()
Checks whether a collection is empty.
System.out.println(list.isEmpty());clear()
Removes all elements.
list.clear();Iterating Through Collections
Collections can be traversed in multiple ways.
Enhanced For Loop
for (String name : names) {
System.out.println(name);
}Using Iterator
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}Using forEach()
names.forEach(name -> System.out.println(name));These different approaches provide flexibility when processing collection data.
The Collections Utility Class
Java provides the Collections utility class containing helpful methods.
Sorting
List<Integer> numbers = new ArrayList<>();numbers.add(30);
numbers.add(10);
numbers.add(20);Collections.sort(numbers);System.out.println(numbers);Output:
[10, 20, 30]Reversing
Collections.reverse(numbers);Finding Maximum Value
System.out.println(Collections.max(numbers));Finding Minimum Value
System.out.println(Collections.min(numbers));Shuffling
Collections.shuffle(numbers);The Collections class saves developers from writing these common algorithms manually.
Choosing the Right Collection
Choosing the correct collection depends on the requirement.
Use ArrayList when:
- Order matters
- Duplicates are allowed
- Frequent index access is needed
Use LinkedList when:
- Frequent additions/removals are needed
- Queue/deque-style operations are required
Use HashSet when:
- Only unique elements are needed
- Order is not important
Use LinkedHashSet when:
- Unique elements are needed
- Insertion order should be preserved
Use TreeSet when:
- Unique elements are needed
- Automatic sorting is required
Use PriorityQueue when:
- Elements must be processed according to priority
Use HashMap when:
- Data needs to be stored as key-value pairs
- Fast lookup by key is needed
A Real-World Example
Imagine building an e-commerce application.
Different situations require different collections.
Shopping Cart
List<String> cart = new ArrayList<>();A customer may add the same product multiple times, and order matters.
Unique Product Categories
Set<String> categories = new HashSet<>();Duplicate categories should not exist.
Processing Orders
Queue<String> orders = new LinkedList<>();Orders may be processed in sequence.
Emergency Delivery Priority
PriorityQueue<Integer> priorityOrders = new PriorityQueue<>();Higher-priority orders can be processed according to defined priority.
Product Lookup
Map<Integer, String> products = new HashMap<>();A product ID can act as the key.
This demonstrates why understanding collections is essential for real-world software development.
Final Thoughts
The Java Collections Framework is much more than a collection of classes. It provides a standardized way to organize and manipulate data efficiently.
The key to mastering Collections is not memorizing every class. Instead, focus on understanding three important questions:
- Do I need duplicates?
- Does the order of elements matter?
- Do I need fast access, insertion, deletion, or sorting?
Once you can answer these questions, choosing the right collection becomes much easier.
The hierarchy starts broadly with Iterable, moves to Collection, and then branches into specialized interfaces such as List, Set, and Queue. Each branch provides different behavior and implementation classes designed for specific use cases.
The real power of the Java Collections Framework is choosing the right data structure for the right problem.
Mastering Collections is a major step toward becoming a strong Java developer, because efficient data management is at the heart of almost every real-world application.








