Introduction
When Java applications need to store and manage multiple objects, choosing the right data structure becomes important.
An array can store multiple values, but it has a fixed size and provides only a limited set of built-in operations.
For example:
1String[] students = new String[3]; 2 3students[0] = "Ankit"; 4students[1] = "Rahul"; 5students[2] = "Priya";
If you later need more than three students, the array itself cannot grow.
Java provides the Java Collections Framework (JCF) to solve many of these problems.
The Java Collections Framework is a unified set of interfaces, implementations, and algorithms for storing, accessing, searching, sorting, and manipulating groups of objects.
Common collection types include:
1List 2Set 3Queue 4Deque 5Map
Different implementations are optimized for different requirements.
For example:
ArrayListis commonly used for general-purpose lists.HashSetis useful when duplicate values should not be stored.TreeSetmaintains sorted unique elements.ArrayDequeis useful for queue and stack-style operations.HashMapprovides efficient key-value lookup.TreeMapmaintains keys in sorted order.
The Collections Framework is fundamental to Java development and is widely used in backend applications, Spring Boot, enterprise software, APIs, and coding interviews.
What Is the Java Collections Framework?
The Java Collections Framework (JCF) is a collection of interfaces and classes designed to represent and manipulate groups of objects.
The framework provides:
- Collection interfaces
- General-purpose implementations
- Specialized implementations
- Iterators
- Utility algorithms
- Sorting support
- Searching operations
- Type-safe generic collections
The most important interfaces include:
1Iterable 2 │ 3 └── Collection 4 ├── List 5 ├── Set 6 └── Queue 7 └── Deque 8 9Map
Map is part of the Java Collections Framework, but it does not extend Collection.
Importing Collections
You can import individual collection classes:
1import java.util.ArrayList; 2import java.util.HashMap; 3import java.util.HashSet;
Or use:
1import java.util.*;
For production applications, importing only the classes you need can make dependencies clearer.
Collection Interface Hierarchy
The major hierarchy can be simplified as follows:
1Iterable 2 │ 3 └── Collection 4 │ 5 ├── List 6 │ ├── ArrayList 7 │ ├── LinkedList 8 │ └── Vector 9 │ 10 ├── Set 11 │ ├── HashSet 12 │ ├── LinkedHashSet 13 │ └── SortedSet 14 │ └── NavigableSet 15 │ └── TreeSet 16 │ 17 └── Queue 18 ├── PriorityQueue 19 └── Deque 20 ├── ArrayDeque 21 └── LinkedList 22 23Map 24 ├── HashMap 25 ├── LinkedHashMap 26 ├── SortedMap 27 │ └── NavigableMap 28 │ └── TreeMap 29 └── Hashtable
This hierarchy helps you understand the relationship between interfaces and their implementations.
For example:
1List<String> names = new ArrayList<>();
The variable is declared using the List interface while the object is created using ArrayList.
This is an example of programming to an interface.
List Interface
A List represents an ordered collection.
A list:
- Maintains element order
- Allows duplicate elements
- Provides index-based access
- Supports dynamic sizing
Example:
1List<String> languages = new ArrayList<>(); 2 3languages.add("Java"); 4languages.add("Python"); 5languages.add("Java"); 6 7System.out.println(languages);
Output:
1[Java, Python, Java]
The duplicate "Java" is allowed.
ArrayList
ArrayList is a resizable-array implementation of the List interface.
It is usually the best default choice when you need a general-purpose list.
Creating an ArrayList
1import java.util.ArrayList; 2import java.util.List; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 List<String> students = new ArrayList<>(); 9 10 students.add("Ankit"); 11 students.add("Rahul"); 12 students.add("Priya"); 13 14 System.out.println(students); 15 } 16}
Output:
1[Ankit, Rahul, Priya]
Accessing Elements
1String firstStudent = students.get(0); 2 3System.out.println(firstStudent);
Output:
1Ankit
Updating an Element
1students.set(1, "Aman"); 2 3System.out.println(students);
Removing an Element
1students.remove("Priya");
Searching
1if (students.contains("Ankit")) { 2 System.out.println("Student found"); 3}
Iterating
The enhanced for loop is often the simplest approach:
1for (String student : students) { 2 System.out.println(student); 3}
Common ArrayList Methods
| Method | Purpose |
|---|---|
add() | Adds an element |
get() | Retrieves an element by index |
set() | Replaces an element |
remove() | Removes an element |
contains() | Checks whether an element exists |
size() | Returns the number of elements |
isEmpty() | Checks whether the list is empty |
clear() | Removes all elements |
When Should You Use ArrayList?
Use ArrayList when:
- You need fast index-based access.
- You frequently iterate through elements.
- Insertions and removals are not predominantly at the beginning or middle of a large list.
- You need a general-purpose
List.
For most ordinary list-based application code, ArrayList is a good starting point.
LinkedList
LinkedList is a doubly linked implementation that implements both List and Deque.
1import java.util.LinkedList; 2 3public class Main { 4 5 public static void main(String[] args) { 6 7 LinkedList<String> cities = new LinkedList<>(); 8 9 cities.add("Delhi"); 10 cities.add("Mumbai"); 11 cities.add("Pune"); 12 13 System.out.println(cities); 14 } 15}
Output:
1[Delhi, Mumbai, Pune]
Because LinkedList also implements Deque, it supports operations at both ends:
1cities.addFirst("Jaipur"); 2cities.addLast("Bangalore"); 3 4System.out.println(cities);
Important LinkedList Consideration
A common misconception is that LinkedList is automatically faster for every insertion and deletion.
It is not.
Finding a position in a linked list can require traversal, and random access such as:
1cities.get(1000);
is much slower than accessing an ArrayList element by index.
Therefore, choose LinkedList for a specific access pattern rather than simply assuming that "linked lists are faster for insertion."
For queue/deque operations, ArrayDeque is often a better choice in modern Java code.
Vector
Vector is a legacy List implementation with synchronized methods.
Example:
1import java.util.Vector; 2 3public class Main { 4 5 public static void main(String[] args) { 6 7 Vector<Integer> numbers = new Vector<>(); 8 9 numbers.add(10); 10 numbers.add(20); 11 numbers.add(30); 12 13 System.out.println(numbers); 14 } 15}
Vector is synchronized, but this does not automatically make an entire application or compound operation thread-safe.
For new code, prefer modern collection choices unless you specifically need Vector for legacy compatibility.
Stack
Stack is a legacy class that represents a LIFO structure.
LIFO means:
1Last In 2First Out
Example:
1import java.util.Stack; 2 3public class Main { 4 5 public static void main(String[] args) { 6 7 Stack<String> books = new Stack<>(); 8 9 books.push("Java"); 10 books.push("Python"); 11 books.push("C++"); 12 13 System.out.println(books.pop()); 14 System.out.println(books.peek()); 15 } 16}
Output:
1C++ 2Python
For new applications, ArrayDeque is generally preferred for stack operations.
Example:
1Deque<String> stack = new ArrayDeque<>(); 2 3stack.push("Java"); 4stack.push("Python"); 5 6System.out.println(stack.pop());
Set Interface
A Set represents a collection that does not allow duplicate elements.
For example:
1Set<String> skills = new HashSet<>(); 2 3skills.add("Java"); 4skills.add("Python"); 5skills.add("Java"); 6 7System.out.println(skills);
The second "Java" is not added.
A set does not provide index-based access like a list.
HashSet
HashSet is a hash-table-based implementation of Set.
It is commonly used when you need:
- Unique values
- Fast average-case lookup
- No requirement for insertion order
- No requirement for sorted order
Example:
1import java.util.HashSet; 2import java.util.Set; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Set<String> skills = new HashSet<>(); 9 10 skills.add("Java"); 11 skills.add("Python"); 12 skills.add("Java"); 13 skills.add("Docker"); 14 15 System.out.println(skills); 16 } 17}
The exact iteration order should not be relied upon.
Do not write code that expects a HashSet to print elements in a particular order.
LinkedHashSet
LinkedHashSet combines hash-based lookup with predictable insertion-order iteration.
Example:
1import java.util.LinkedHashSet; 2import java.util.Set; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Set<String> technologies = 9 new LinkedHashSet<>(); 10 11 technologies.add("Java"); 12 technologies.add("Docker"); 13 technologies.add("Kubernetes"); 14 technologies.add("Java"); 15 16 System.out.println(technologies); 17 } 18}
Output:
1[Java, Docker, Kubernetes]
The duplicate "Java" is ignored while the insertion order is preserved.
TreeSet
TreeSet stores unique elements in sorted order.
Example:
1import java.util.Set; 2import java.util.TreeSet; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Set<Integer> numbers = new TreeSet<>(); 9 10 numbers.add(50); 11 numbers.add(10); 12 numbers.add(30); 13 numbers.add(10); 14 15 System.out.println(numbers); 16 } 17}
Output:
1[10, 30, 50]
TreeSet is useful when you need both:
- Uniqueness
- Sorted ordering
Elements must have an appropriate natural ordering or be supplied with a compatible Comparator.
Queue Interface
A queue is designed for processing elements in a particular order.
A common queue model is FIFO:
1First In 2First Out
For example:
1Customer A → Customer B → Customer C 2 3Customer A is processed first.
Queue with ArrayDeque
For a general-purpose FIFO queue, ArrayDeque is often a good choice.
1import java.util.ArrayDeque; 2import java.util.Queue; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Queue<String> customers = new ArrayDeque<>(); 9 10 customers.offer("Customer A"); 11 customers.offer("Customer B"); 12 customers.offer("Customer C"); 13 14 System.out.println(customers.poll()); 15 System.out.println(customers); 16 } 17}
Output:
1Customer A 2[Customer B, Customer C]
Useful queue methods include:
| Method | Behavior |
|---|---|
offer() | Adds an element |
poll() | Removes and returns the front element |
peek() | Returns the front element without removing it |
The offer(), poll(), and peek() methods are often preferable for queue APIs because they provide non-exception-based behavior when an operation cannot be performed.
PriorityQueue
PriorityQueue processes elements according to priority rather than simple insertion order.
By default, elements are ordered according to their natural ordering.
Example:
1import java.util.PriorityQueue; 2import java.util.Queue; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Queue<Integer> queue = 9 new PriorityQueue<>(); 10 11 queue.offer(30); 12 queue.offer(10); 13 queue.offer(20); 14 15 while (!queue.isEmpty()) { 16 System.out.println(queue.poll()); 17 } 18 } 19}
Output:
110 220 330
A PriorityQueue is useful for:
- Task scheduling
- Priority-based processing
- Algorithms
- Graph problems
- Job queues
Important: iterating over a PriorityQueue does not guarantee that elements will appear in priority order. Use poll() to repeatedly retrieve elements according to the queue's ordering.
Deque
Deque means Double-Ended Queue.
It supports insertion and removal from both ends.
1import java.util.ArrayDeque; 2import java.util.Deque; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Deque<Integer> deque = new ArrayDeque<>(); 9 10 deque.addFirst(10); 11 deque.addLast(20); 12 deque.addFirst(5); 13 14 System.out.println(deque); 15 16 System.out.println( 17 "First: " + deque.peekFirst() 18 ); 19 20 System.out.println( 21 "Last: " + deque.peekLast() 22 ); 23 } 24}
Output:
1[5, 10, 20] 2First: 5 3Last: 20
ArrayDeque can also be used as a stack:
1Deque<String> stack = new ArrayDeque<>(); 2 3stack.push("Java"); 4stack.push("Python"); 5 6System.out.println(stack.pop());
Output:
1Python
Map Interface
A Map stores associations between keys and values.
For example:
1Student ID → Student Name 2 3101 → Ankit 4102 → Rahul 5103 → Priya
A map:
- Stores key-value pairs
- Does not allow duplicate keys
- Can allow duplicate values
- Provides lookup based on keys
Example:
1Map<Integer, String> students = new HashMap<>(); 2 3students.put(101, "Ankit"); 4students.put(102, "Rahul"); 5students.put(103, "Priya");
If an existing key is inserted again, its associated value is replaced:
1students.put(101, "Aman");
Now key 101 maps to "Aman".
HashMap
HashMap is the most common general-purpose map implementation.
Example:
1import java.util.HashMap; 2import java.util.Map; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Map<Integer, String> students = 9 new HashMap<>(); 10 11 students.put(101, "Ankit"); 12 students.put(102, "Rahul"); 13 students.put(103, "Priya"); 14 15 String student = 16 students.get(102); 17 18 System.out.println(student); 19 } 20}
Output:
1Rahul
Checking Whether a Key Exists
1if (students.containsKey(101)) { 2 System.out.println("Student exists"); 3}
Iterating Over a Map
A common approach is to use entrySet():
1for (Map.Entry<Integer, String> entry 2 : students.entrySet()) { 3 4 System.out.println( 5 entry.getKey() + " : " 6 + entry.getValue() 7 ); 8}
This provides both the key and value efficiently.
LinkedHashMap
LinkedHashMap maintains a predictable insertion-order iteration.
Example:
1import java.util.LinkedHashMap; 2import java.util.Map; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Map<Integer, String> students = 9 new LinkedHashMap<>(); 10 11 students.put(101, "Ankit"); 12 students.put(102, "Rahul"); 13 students.put(103, "Priya"); 14 15 System.out.println(students); 16 } 17}
Output:
1{101=Ankit, 102=Rahul, 103=Priya}
It is useful when you need map lookup while also preserving insertion order.
TreeMap
TreeMap stores keys in sorted order.
Example:
1import java.util.Map; 2import java.util.TreeMap; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Map<Integer, String> students = 9 new TreeMap<>(); 10 11 students.put(103, "Priya"); 12 students.put(101, "Ankit"); 13 students.put(102, "Rahul"); 14 15 System.out.println(students); 16 } 17}
Output:
1{101=Ankit, 102=Rahul, 103=Priya}
Use TreeMap when sorted keys or navigable-map operations are required.
Hashtable
Hashtable is a legacy synchronized implementation of Map.
Example:
1import java.util.Hashtable; 2import java.util.Map; 3 4public class Main { 5 6 public static void main(String[] args) { 7 8 Map<Integer, String> table = 9 new Hashtable<>(); 10 11 table.put(1, "Java"); 12 table.put(2, "Python"); 13 14 System.out.println(table); 15 } 16}
Hashtable does not permit null keys or null values.
For new applications, HashMap or an appropriate modern concurrent collection is generally preferred.
Collection Time Complexity
Understanding approximate operation complexity helps when selecting a collection.
| Collection | Access/Search | Add | Remove | Ordering |
|---|---|---|---|---|
ArrayList | O(1) index access | O(1) amortized at end | O(n) middle | Insertion order |
LinkedList | O(n) | O(1) at known node/end | O(1) at known node/end | Insertion order |
HashSet | O(1) average lookup | O(1) average | O(1) average | No guaranteed order |
LinkedHashSet | O(1) average | O(1) average | O(1) average | Insertion order |
TreeSet | O(log n) | O(log n) | O(log n) |
These are typical complexity characteristics, not guarantees that every operation on every implementation will behave identically in every situation.
List Comparison
| Collection | Duplicates | Index Access | Ordering | Recommended Use |
|---|---|---|---|---|
ArrayList | Yes | Fast | Insertion order | General-purpose lists |
LinkedList | Yes | Slow | Insertion order | Specialized linked/deque operations |
Vector | Yes | Fast | Insertion order | Legacy compatibility |
Stack | Yes | Available | LIFO operations | Legacy code |
For new stack implementations, prefer:
1Deque<String> stack = new ArrayDeque<>();
rather than:
1Stack<String> stack = new Stack<>();
Set Comparison
| Collection | Duplicates | Ordering |
|---|---|---|
HashSet | No | No guaranteed order |
LinkedHashSet | No | Insertion order |
TreeSet | No | Sorted order |
Map Comparison
| Collection | Duplicate Keys | Ordering |
|---|---|---|
HashMap | No | No guaranteed order |
LinkedHashMap | No | Insertion order |
TreeMap | No | Sorted by key |
Hashtable | No | No guaranteed order |
Choosing the Right Collection
The best collection depends on what your application needs.
| Requirement | Recommended Choice |
|---|---|
| General-purpose list | ArrayList |
| Fast index-based access | ArrayList |
| Unique values | HashSet |
| Unique values with insertion order | LinkedHashSet |
| Sorted unique values | TreeSet |
| FIFO queue | ArrayDeque |
| LIFO stack | ArrayDeque |
| Priority-based processing | PriorityQueue |
| General key-value lookup | HashMap |
| Key-value lookup with insertion order | LinkedHashMap |
| Sorted key-value mapping | TreeMap |
A good rule is to start with the simplest collection that matches the requirements rather than choosing a collection based only on popularity.
Programming to Interfaces
Prefer declaring variables using interfaces:
1List<String> names = new ArrayList<>(); 2 3Set<String> skills = new HashSet<>(); 4 5Map<Integer, String> students = 6 new HashMap<>(); 7 8Queue<String> queue = 9 new ArrayDeque<>();
instead of unnecessarily exposing the implementation:
1ArrayList<String> names = 2 new ArrayList<>();
Programming to interfaces makes it easier to replace the implementation later.
For example:
1List<String> names = new ArrayList<>();
can later become:
1List<String> names = new LinkedList<>();
without changing code that only depends on the List interface.
Generics and Collections
Collections should generally be used with generics.
Avoid raw collections:
1List students = new ArrayList();
Prefer:
1List<String> students = 2 new ArrayList<>();
Generics provide compile-time type checking.
For example:
1List<String> students = 2 new ArrayList<>(); 3 4students.add("Ankit"); 5 6// students.add(100); // Compile-time error
This prevents many type-related runtime errors.
Collection Utility Methods
Java also provides utility methods through Collections.
For example:
1import java.util.ArrayList; 2import java.util.Collections; 3import java.util.List; 4 5public class Main { 6 7 public static void main(String[] args) { 8 9 List<Integer> numbers = 10 new ArrayList<>(); 11 12 numbers.add(40); 13 numbers.add(10); 14 numbers.add(30); 15 numbers.add(20); 16 17 Collections.sort(numbers); 18 19 System.out.println(numbers); 20 21 int maximum = 22 Collections.max(numbers); 23 24 int minimum = 25 Collections.min(numbers); 26 27 System.out.println( 28 "Maximum: " + maximum 29 ); 30 31 System.out.println( 32 "Minimum: " + minimum 33 ); 34 } 35}
Output:
1[10, 20, 30, 40] 2Maximum: 40 3Minimum: 10
The Collections utility class provides many useful operations for working with collections.
Immutable Collections
Modern Java provides convenient factory methods for creating unmodifiable collections.
For example:
1List<String> languages = 2 List.of("Java", "Python", "C++"); 3 4Set<String> skills = 5 Set.of("Java", "Docker", "Linux"); 6 7Map<Integer, String> students = 8 Map.of( 9 101, "Ankit", 10 102, "Rahul" 11 );
These collections should not be modified.
For example:
1languages.add("Go");
will result in an UnsupportedOperationException.
Use these factory methods when you need a fixed, unmodifiable collection.
Best Practices
Prefer Interfaces
Use:
1List<String> names = new ArrayList<>();
instead of exposing the implementation unnecessarily.
Choose Based on Access Patterns
Do not choose a collection simply because it is popular.
Think about:
- How often elements are read
- Whether index access is required
- Whether duplicates are allowed
- Whether ordering matters
- Whether sorting is required
- Whether priority processing is required
- Whether concurrent access is required
Use ArrayList as the Default List
For many ordinary list use cases:
1List<T> items = new ArrayList<>();
is a strong default.
Avoid Legacy Collections in New Code
Prefer:
1Deque<T> stack = new ArrayDeque<>();
over:
1Stack<T> stack = new Stack<>();
Similarly, Hashtable and Vector are generally legacy choices.
Use Generics
Always prefer parameterized collections:
1List<Student> students = 2 new ArrayList<>();
Do Not Rely on HashMap or HashSet Ordering
Do not assume:
1HashMap 2HashSet
will preserve insertion order.
If ordering is required, use an appropriate ordered implementation such as:
1LinkedHashMap 2LinkedHashSet
or a sorted implementation such as:
1TreeMap 2TreeSet
Practice Project: Student Management System
Let's build a small student management example using multiple collection types.
1import java.util.ArrayList; 2import java.util.HashMap; 3import java.util.List; 4import java.util.Map; 5 6record Student( 7 int id, 8 String name, 9 String course 10) { 11}
The service can maintain students using a Map:
1class StudentService { 2 3 private final Map<Integer, Student> students = 4 new HashMap<>(); 5 6 public void addStudent(Student student) { 7 8 if (students.containsKey(student.id())) { 9 throw new IllegalArgumentException( 10 "Student ID already exists: " 11 + student.id() 12 ); 13 } 14 15 students.put(student.id(), student); 16 } 17 18 public Student findStudentById(int id) { 19 20 return students.get(id); 21 } 22 23 public List<Student> getAllStudents() { 24 25 return new ArrayList<>(students.values()); 26 } 27 28 public boolean removeStudent(int id) { 29 30 return students.remove(id) != null; 31 } 32}
Now use the service:
1public class Main { 2 3 public static void main(String[] args) { 4 5 StudentService service = 6 new StudentService(); 7 8 service.addStudent( 9 new Student(101, "Ankit", "Java") 10 ); 11 12 service.addStudent( 13 new Student(102, "Rahul", "Spring Boot") 14 ); 15 16 service.addStudent( 17 new Student(103, "Priya", "Python") 18 ); 19 20 Student student = 21 service.findStudentById(102); 22 23 System.out.println(student); 24 25 System.out.println("\nAll Students:"); 26 27 for (Student item : service.getAllStudents()) { 28 System.out.println(item); 29 } 30 31 service.removeStudent(101); 32 33 System.out.println( 34 "\nAfter deleting student 101:" 35 ); 36 37 for (Student item : service.getAllStudents()) { 38 System.out.println(item); 39 } 40 } 41}
This example demonstrates a practical reason for choosing a Map: student IDs are unique and are used to look up students.
Instead of searching through a list every time:
1for (Student student : students) { 2 if (student.id() == id) { 3 // ... 4 } 5}
the map can directly associate:
1Student ID → Student
This is a common pattern in real-world applications.
Practice Project: Inventory Management
A map is also useful for inventory systems.
1import java.util.HashMap; 2import java.util.Map; 3 4public class InventoryService { 5 6 private final Map<String, Integer> inventory = 7 new HashMap<>(); 8 9 public void addProduct( 10 String product, 11 int quantity 12 ) { 13 14 if (quantity < 0) { 15 throw new IllegalArgumentException( 16 "Quantity cannot be negative." 17 ); 18 } 19 20 inventory.merge( 21 product, 22 quantity, 23 Integer::sum 24 ); 25 } 26 27 public int getQuantity(String product) { 28 29 return inventory.getOrDefault(product, 0); 30 } 31 32 public void displayInventory() { 33 34 for (Map.Entry<String, Integer> entry 35 : inventory.entrySet()) { 36 37 System.out.println( 38 entry.getKey() 39 + " : " 40 + entry.getValue() 41 ); 42 } 43 } 44}
Usage:
1public class Main { 2 3 public static void main(String[] args) { 4 5 InventoryService inventory = 6 new InventoryService(); 7 8 inventory.addProduct("Laptop", 10); 9 inventory.addProduct("Mouse", 50); 10 inventory.addProduct("Laptop", 5); 11 12 System.out.println( 13 "Laptop stock: " 14 + inventory.getQuantity("Laptop") 15 ); 16 17 System.out.println("\nInventory:"); 18 19 inventory.displayInventory(); 20 } 21}
Output includes:
1Laptop stock: 15
The Map provides a natural representation for product-to-quantity relationships.
Common Interview Questions
What Is the Difference Between List and Set?
List allows duplicates and provides ordered, index-based access.
Set does not allow duplicate elements and generally does not provide index-based access.
What Is the Difference Between ArrayList and LinkedList?
ArrayList is backed by a resizable array and provides efficient random access.
LinkedList is a linked structure and provides efficient operations at known ends or nodes, but random access is slower.
What Is the Difference Between HashSet and TreeSet?
HashSet provides hash-based storage without guaranteed ordering.
TreeSet maintains sorted order and typically provides O(log n) operations.
What Is the Difference Between HashMap and TreeMap?
HashMap provides average constant-time key lookup and does not guarantee key ordering.
TreeMap keeps keys sorted and generally provides O(log n) operations.
Is Map a Collection?
No.
Map belongs to the Java Collections Framework, but it does not extend the Collection interface.
Which Collection Is Best for a Stack?
For new code, a common choice is:
1Deque<T> stack = new ArrayDeque<>();
Which Collection Should I Use for Unique Values?
Use HashSet when ordering is not required.
Use LinkedHashSet when insertion order matters.
Use TreeSet when sorted order is required.
Summary
The Java Collections Framework provides reusable data structures and algorithms for managing groups of objects.
In this tutorial, you learned:
- What the Java Collections Framework is
- The major collection interfaces
- Why
Mapis separate from theCollectionhierarchy - How
Listworks - How to use
ArrayList - When
LinkedListcan be useful - Why
VectorandStackare considered legacy choices - How
Setprevents duplicate values - How
HashSet,LinkedHashSet, andTreeSetdiffer - How queues work
- How
ArrayDequecan be used for queues and stacks - How
PriorityQueueprocesses elements by priority - How
Dequesupports both ends - How
Mapstores key-value pairs - How
HashMap,LinkedHashMap, andTreeMapdiffer - Why
Hashtableis considered a legacy collection - Basic collection time complexities
- How to choose the right collection
- Why programming to interfaces is useful
- How generics improve collection type safety
- How to use immutable collection factory methods
- How collections can be applied to real-world applications
The most important skill is not memorizing every collection class. Instead, understand the requirements of your application and select a collection based on ordering, uniqueness, lookup performance, access patterns, memory considerations, and concurrency requirements.
Practice Exercises
Exercise 1: Contact Manager
Create a contact management application using ArrayList.
Implement:
1addContact() 2updateContact() 3deleteContact() 4searchContact() 5displayContacts()
Exercise 2: Unique Email Registry
Use HashSet<String> to create a registration system that prevents duplicate email addresses.
Implement:
1registerEmail() 2removeEmail() 3emailExists() 4displayEmails()
Exercise 3: Ordered History
Use LinkedHashSet to store recently visited website URLs while preventing duplicates and preserving insertion order.
Exercise 4: Sorted Student Marks
Use TreeMap<Integer, String> to associate student marks with student names and display the entries in sorted key order.
Exercise 5: Task Scheduler
Create a Task class containing:
1id 2name 3priority
Use PriorityQueue to process the highest-priority tasks first.
Exercise 6: Stack Calculator
Use:
1Deque<Integer> stack = new ArrayDeque<>();
to implement:
- Push
- Pop
- Peek
- Is empty
- Size
Exercise 7: Library Management System
Build a small library application that uses:
List<Book>for booksSet<String>for unique categoriesMap<Integer, Book>for book lookup by IDQueue<Book>for borrowing requestsDeque<Book>for recently returned books
The goal is to understand why different collections are appropriate for different data-management requirements.