When a Java program needs to work with a group of related values, an array is often the first data structure beginners learn. Arrays are useful, but their length is fixed after creation. That becomes inconvenient when the number of elements changes during program execution.
Java provides the Collections Framework to make this type of programming easier. It contains interfaces, classes, and utility methods for storing and processing groups of objects. Instead of building common data structures from scratch, a developer can select an existing collection according to the requirements of the program.
For example, an application may need an ordered list of products, a collection containing only unique course codes, a queue of tasks waiting for processing, or a structure that connects student IDs with student names. Different collection types are designed for these different situations.
This tutorial explains the main collection interfaces and several commonly used implementations, with small Java programs that demonstrate how they behave.
The Java Collections Framework is a standard library architecture for representing and manipulating groups of objects. It provides interfaces such as List, Set, Queue, and Map, together with classes that implement these interfaces.
The important idea is that the interface describes the type of collection behavior, while an implementation provides the actual data structure and operational characteristics.
import java.util.ArrayList;
import java.util.List;
public class CollectionIntro {
public static void main(String[] args) {
List<String> subjects = new ArrayList<>();
subjects.add("Java");
subjects.add("DBMS");
subjects.add("Computer Networks");
System.out.println(subjects);
}
}
[Java, DBMS, Computer Networks]
Here, List describes the collection behavior and ArrayList provides the implementation. The list can hold multiple values and can grow when new elements are added.
The main purpose of collections is to provide convenient and flexible ways to manage groups of objects. The best collection depends on what the program needs to do with the data.
Consider a student-management application. Student names may need to remain in a particular order, registration numbers must be unique, pending requests may need to be processed one by one, and student records may need to be retrieved using an ID. These requirements do not have the same data structure.
| Requirement | Suitable Collection |
|---|---|
| Maintain an ordered group of values | List |
| Prevent duplicate values | Set |
| Process waiting items in sequence | Queue |
| Associate one value with another using a key | Map |
The Collections Framework contains several related interfaces. List, Set, and Queue are part of the main collection hierarchy. Map is different because it stores associations between keys and values rather than individual collection elements.
| Interface | Main Idea | Common Implementations |
|---|---|---|
| List | Ordered elements; duplicates are allowed. | ArrayList, LinkedList |
| Set | Unique elements. | HashSet, LinkedHashSet, TreeSet |
| Queue | Elements waiting to be processed. | LinkedList, PriorityQueue |
| Map | Key-value associations. | HashMap, LinkedHashMap, TreeMap |
A List represents an ordered sequence of elements. A list allows duplicate values, and elements can normally be accessed using their position, called an index.
Lists are useful when the order of elements matters or when a program needs to retrieve an element by its position.
import java.util.ArrayList;
import java.util.List;
public class ListExample {
public static void main(String[] args) {
List<String> chapters = new ArrayList<>();
chapters.add("Variables");
chapters.add("Methods");
chapters.add("Arrays");
chapters.add("Methods");
System.out.println(chapters);
System.out.println(chapters.get(2));
}
}
[Variables, Methods, Arrays, Methods] Arrays
The example demonstrates two important properties of a List: the insertion order is retained and the same value can occur more than once.
ArrayList is a resizable-array implementation of the List interface. It is a common choice when a program frequently reads elements by index and does not need constant insertion or removal at arbitrary positions.
import java.util.ArrayList;
public class ArrayListExample {
public static void main(String[] args) {
ArrayList<String> courses = new ArrayList<>();
courses.add("Java");
courses.add("Python");
courses.add("SQL");
System.out.println("Courses: " + courses);
courses.add("C++");
System.out.println("After adding: " + courses);
courses.remove("Python");
System.out.println("After removal: " + courses);
System.out.println("Second course: " + courses.get(1));
}
}
Courses: [Java, Python, SQL] After adding: [Java, Python, SQL, C++] After removal: [Java, SQL, C++] Second course: SQL
| Method | Purpose |
|---|---|
| add() | Adds an element. |
| get() | Retrieves an element using its index. |
| set() | Replaces an element at a particular index. |
| remove() | Removes an element. |
| contains() | Checks whether a value exists. |
| size() | Returns the number of elements. |
LinkedList is another implementation of the List interface. It is based on linked nodes, where each node stores an element together with links to other nodes.
LinkedList also implements the Queue and Deque interfaces, which makes it useful when a program needs operations at both ends of a sequence.
import java.util.LinkedList;
public class LinkedListExample {
public static void main(String[] args) {
LinkedList<String> tasks = new LinkedList<>();
tasks.add("Compile Program");
tasks.add("Run Tests");
tasks.addFirst("Open Project");
tasks.addLast("Close Project");
System.out.println(tasks);
}
}
[Open Project, Compile Program, Run Tests, Close Project]
The example uses operations at both ends of the list. This is one reason LinkedList can be useful when an application naturally works with the beginning and end of a sequence.
| Feature | ArrayList | LinkedList |
|---|---|---|
| Underlying structure | Resizable array | Linked nodes |
| Access by index | Efficient | Requires traversal |
| Adding/removing at ends | Supported | Efficiently supported |
| Typical use | Frequent reading and indexed access | Sequence operations involving links or both ends |
The choice should be based on the actual access pattern of the application. It is not correct to assume that LinkedList is automatically faster for every insertion or deletion. The position of the operation and the cost of finding that position also matter.
A Set represents a collection in which duplicate elements are not permitted. It is useful when uniqueness is part of the requirement rather than something that must be checked manually.
For example, an application collecting unique course codes can use a Set instead of repeatedly checking whether a code has already been added.
HashSet is a commonly used Set implementation. It uses hashing to organize elements and does not guarantee a predictable iteration order.
import java.util.HashSet;
public class HashSetExample {
public static void main(String[] args) {
HashSet<String> courseCodes = new HashSet<>();
courseCodes.add("CS101");
courseCodes.add("CS205");
courseCodes.add("CS101");
System.out.println("Number of unique courses: "
+ courseCodes.size());
System.out.println(courseCodes);
}
}
Number of unique courses: 2 [CS101, CS205]
The exact order displayed by a HashSet should not be relied upon. The important property demonstrated here is that adding the same value again does not create another copy.
HashSet is appropriate when uniqueness is more important than maintaining a particular iteration order.
For example, it can be useful for storing unique usernames, distinct subject codes, unique product IDs, or already-processed identifiers.
If the program must preserve insertion order, a different Set implementation such as LinkedHashSet may be more suitable.
A Queue is designed for elements that are waiting to be processed. A common queue model is FIFO, meaning First In, First Out. The element that enters first is normally processed first.
Queues are useful in task processing, request handling, print scheduling, and other situations where work arrives and is handled in sequence.
import java.util.LinkedList;
import java.util.Queue;
public class QueueExample {
public static void main(String[] args) {
Queue<String> tasks = new LinkedList<>();
tasks.offer("Compile");
tasks.offer("Test");
tasks.offer("Deploy");
System.out.println("Next task: " + tasks.peek());
String completed = tasks.poll();
System.out.println("Completed: " + completed);
System.out.println("Remaining: " + tasks);
}
}
Next task: Compile Completed: Compile Remaining: [Test, Deploy]
| Method | Purpose |
|---|---|
| offer() | Adds an element to the queue. |
| peek() | Reads the front element without removing it. |
| poll() | Removes and returns the front element. |
A Map stores information as key-value pairs. Instead of accessing an item by a numeric index, a program can use a key to find its associated value.
For example, a student ID can act as a key while the student's name can be the corresponding value.
HashMap is a widely used Map implementation. Each key can occur only once, while multiple keys may have the same value.
import java.util.HashMap;
import java.util.Map;
public class HashMapExample {
public static void main(String[] args) {
Map<Integer, String> students = new HashMap<>();
students.put(101, "Aman");
students.put(102, "Neha");
students.put(103, "Ravi");
System.out.println("Student 102: "
+ students.get(102));
System.out.println("All student records:");
for (Map.Entry<Integer, String> entry
: students.entrySet()) {
System.out.println(
entry.getKey() + " : " + entry.getValue()
);
}
}
}
Student 102: Neha All student records: 101 : Aman 102 : Neha 103 : Ravi
The put() method creates a key-value association, while get() retrieves the value for a particular key. The entrySet() method provides the entries so that both the key and value can be processed together.
The order shown while iterating through a HashMap should not be treated as guaranteed. If a program needs predictable ordering, another Map implementation may be more appropriate.
A Map does not store two separate entries for the same key. If an existing key is used again with a new value, the value associated with that key is replaced.
import java.util.HashMap;
public class DuplicateKeyExample {
public static void main(String[] args) {
HashMap<Integer, String> students = new HashMap<>();
students.put(101, "Aman");
students.put(101, "Rohit");
System.out.println(students.get(101));
}
}
Rohit
The key 101 remains the same, but its associated value changes from Aman to Rohit.
| Property | List | Set | Queue | Map |
|---|---|---|---|---|
| Stores | Elements | Unique elements | Elements awaiting processing | Key-value pairs |
| Duplicates | Allowed | Not allowed | Depends on implementation | Keys cannot duplicate |
| Typical access | Index | Element | Front of queue | Key |
| Common implementation | ArrayList | HashSet | LinkedList | HashMap |
A collection can be traversed in several ways. The enhanced for loop is a simple option when the program needs to process each element without manually managing an index.
import java.util.ArrayList;
public class IterationExample {
public static void main(String[] args) {
ArrayList<String> topics = new ArrayList<>();
topics.add("Classes");
topics.add("Inheritance");
topics.add("Collections");
for (String topic : topics) {
System.out.println("Topic: " + topic);
}
}
}
Topic: Classes Topic: Inheritance Topic: Collections
Java collections are commonly used with generics. Generics specify the type of elements that a collection is expected to contain.
ArrayList<Integer> marks = new ArrayList<>(); marks.add(75); marks.add(82); marks.add(91);
In this example, the collection is intended to contain Integer values. Generics improve type safety and reduce the need for manual type casting when values are retrieved.
There is no single collection that is best for every problem. A useful way to select a collection is to first identify what operation the application performs most often.
| Requirement | Possible Choice | Reason |
|---|---|---|
| Ordered data with frequent index access | ArrayList | Efficient indexed access. |
| Unique values | HashSet | Duplicates are not stored. |
| Key-value lookup | HashMap | Values can be associated with keys. |
| Processing items as they arrive | Queue | Provides queue-oriented operations. |
| Operations at both ends of a sequence | Deque / LinkedList | Supports insertion and removal at both ends. |
Different collection implementations have different ordering characteristics. For example, HashSet and HashMap should not be used when the program depends on a predictable iteration order.
If duplicate values are not meaningful, using a Set may express the requirement more clearly than adding duplicate checks around a List.
Insertion performance depends on where the insertion occurs and whether the required position has already been located. Therefore, LinkedList should not be selected simply because an application performs insertions.
Using raw collection types removes some of the compile-time type checking provided by generics. Prefer declarations such as List<String> instead of raw List whenever possible.
A Map does not use positional indexes in the same way a List does. A key identifies the associated value, and that key should be chosen according to the application's data model.
Java also provides the Collections utility class in the java.util package. It contains methods that can perform common operations on collection objects.
import java.util.ArrayList;
import java.util.Collections;
public class SortingExample {
public static void main(String[] args) {
ArrayList<Integer> marks = new ArrayList<>();
marks.add(72);
marks.add(45);
marks.add(91);
marks.add(68);
Collections.sort(marks);
System.out.println(marks);
}
}
[45, 68, 72, 91]
The example shows how the utility class can be used to sort a mutable List without implementing a sorting algorithm manually.
| Array | Collection |
|---|---|
| Length is fixed after creation. | Many implementations can grow or shrink dynamically. |
| Can store primitive values directly. | Collections store objects and work with wrapper types for primitives. |
| Provides basic indexing. | Provides many built-in operations through collection APIs. |
| Useful for fixed-size data. | Useful when data management requirements are more flexible. |
Collections appear throughout Java applications because software frequently works with groups of related information.
A simple decision process can help when choosing a collection.
It is a group of interfaces, implementations, and utility classes used for storing and processing groups of objects in Java.
A List can contain duplicate elements and represents an ordered sequence, while a Set is designed to contain unique elements.
ArrayList is a resizable-array implementation of the List interface and is commonly used when indexed access is important.
HashSet is a Set implementation used when duplicate values should not be stored and a specific iteration order is not required.
HashMap is a Map implementation that stores associations between keys and values. Keys are unique within the map.
No. If the same key is inserted again, the new value replaces the value previously associated with that key.
A Queue is a collection designed for elements that are waiting to be processed. Many queue implementations follow FIFO behavior.
Generics specify the expected element type and provide stronger compile-time type checking, making collection code safer and easier to work with.
The Java Collections Framework provides a standard way to store and process groups of objects. Instead of relying only on fixed-size arrays, Java programs can use collection interfaces and implementations that match the requirements of the application.
The most important concepts are List, Set, Queue, and Map. ArrayList and LinkedList are commonly used List implementations, HashSet is useful for unique values, Queue is appropriate for processing items in sequence, and HashMap is useful when information needs to be accessed through keys.
The most important lesson is not to memorize one collection as the "best" option. Instead, examine how the data will be stored, accessed, searched, inserted, removed, and ordered, and then choose the collection that fits those requirements.
After understanding collections, the next useful step is learning how Java programs work with files. File handling allows an application to create, read, update, and write information outside the running program.