Collections Framework

The Java Collections Framework (JCF) is an important part of Java programming. It provides a set of interfaces and classes that help developers store, manage, search, sort, and manipulate groups of objects efficiently.

Instead of working with fixed-size arrays, we can use collections that provide dynamic storage and various data management capabilities.

In this tutorial, we will understand six important Java collections with real-world examples.

1. ArrayList – Store Duplicate Elements with Index-Based Access

What is ArrayList?

ArrayList is a resizable array implementation of the List interface.

It allows duplicate elements and maintains insertion order. We can access and update elements using their index.

Real-world example: Shopping Cart

Imagine an e-commerce application where a customer adds products to a shopping cart. A customer can add the same product multiple times.

Java Example

import java.util.*;

public class Main {
    public static void main(String[] args) {

        ArrayList<String> cart = new ArrayList<>();

        cart.add("Laptop");
        cart.add("Mobile");
        cart.add("Laptop");
        cart.add("Keyboard");

        // Display all products
        System.out.println(cart);

        // Access using index
        System.out.println(cart.get(0));

        // Update product
        cart.set(1, "iPhone");

        // Remove product using index
        cart.remove(2);

        System.out.println(cart);
    }
}

Output:

[Laptop, Mobile, Laptop, Keyboard]
Laptop
[Laptop, iPhone, Keyboard]

Important Features:

  • Allows duplicate elements.
  • Maintains insertion order.
  • Supports index-based access.
  • Dynamic in size.
  • Fast random access by index.

When should we use ArrayList?

Use ArrayList when you need to store multiple elements, allow duplicates, and access elements using their index.

2. HashSet – Store Unique Elements

What is HashSet?

HashSet is an implementation of the Set interface. It stores unique elements and does not allow duplicates.

It does not guarantee insertion order.

Real-world example: Unique Email Registrations

Imagine a website where users register using their email addresses. We don’t want duplicate email addresses in our collection.

Java Example

import java.util.*;

public class Main {
    public static void main(String[] args) {

        HashSet<String> emails = new HashSet<>();

        emails.add("amit@gmail.com");
        emails.add("rahul@gmail.com");
        emails.add("amit@gmail.com");
        emails.add("priya@gmail.com");

        // Display emails
        System.out.println(emails);

        // Check whether email exists
        System.out.println(
            emails.contains("amit@gmail.com")
        );
    }
}

Possible Output:

[rahul@gmail.com, amit@gmail.com, priya@gmail.com]
true

Important Features:

  • Does not allow duplicate elements.
  • Does not guarantee insertion order.
  • Does not support index-based access.
  • Uses hashing internally.
  • Allows one null element.

When should we use HashSet?

Use HashSet when you want to maintain a collection of unique elements, such as email addresses, employee IDs, or product categories.

3. HashMap – Store Data in Key-Value Pairs

What is HashMap?

HashMap is an implementation of the Map interface. It stores data in key-value pairs.

Each key must be unique, but multiple keys can have the same value.

Real-world example: Employee Database

Imagine an application where each employee has a unique employee ID and an employee name.

We can use the employee ID as the key and the employee name as the value.

Java Example

import java.util.*;

public class Main {
    public static void main(String[] args) {

        HashMap<Integer, String> employees =
            new HashMap<>();

        employees.put(101, "Amit");
        employees.put(102, "Rahul");
        employees.put(103, "Priya");

        // Get employee using ID
        System.out.println(employees.get(102));

        // Add new employee
        employees.put(104, "Neha");

        // Update employee
        employees.put(102, "Rahul Sharma");

        // Display all employees
        System.out.println(employees);
    }
}

Output:

Rahul
{101=Amit, 102=Rahul Sharma, 103=Priya, 104=Neha}

Important Features:

  • Stores data in key-value pairs.
  • Keys must be unique.
  • Values can be duplicated.
  • Does not guarantee insertion order.
  • Allows one null key and multiple null values.

When should we use HashMap?

Use HashMap when you want to retrieve information using a unique key, such as employee ID, product ID, customer ID, or username.

4. TreeMap – Store Key-Value Pairs in Sorted Order

What is TreeMap?

TreeMap is an implementation of the NavigableMap interface. It stores key-value pairs and automatically sorts entries based on their keys.

By default, keys are sorted in ascending natural order.

Real-world example: Student Records

Imagine a college application where student records should be displayed in ascending order of roll numbers.

Java Example

import java.util.*;

public class Main {
    public static void main(String[] args) {

        TreeMap<Integer, String> students =
            new TreeMap<>();

        students.put(105, "Amit");
        students.put(102, "Rahul");
        students.put(104, "Priya");
        students.put(101, "Neha");

        // Display sorted student records
        System.out.println(students);
    }
}

Output:

{101=Neha, 102=Rahul, 104=Priya, 105=Amit}

Important Features:

  • Stores key-value pairs.
  • Automatically sorts keys.
  • Keys must be unique.
  • Does not allow null keys with natural ordering.
  • Supports operations such as firstKey(), lastKey(), and higherKey().

When should we use TreeMap?

Use TreeMap when you need key-value storage and want the keys to remain sorted automatically.

5. Queue – Process Elements Using FIFO

What is Queue?

Queue is an interface in Java that follows the FIFO (First In, First Out) principle.

The element inserted first is generally the first element to be removed.

Real-world example: Bank Customer Queue

Imagine customers waiting at a bank counter. The first customer who enters the queue gets served first.

Java Example

import java.util.*;

public class Main {
    public static void main(String[] args) {

        Queue<String> customers =
            new LinkedList<>();

        customers.add("Amit");
        customers.add("Rahul");
        customers.add("Priya");

        // Display queue
        System.out.println(customers);

        // Serve first customer
        System.out.println(
            "Serving: " + customers.poll()
        );

        // Display remaining customers
        System.out.println(customers);
    }
}

Output:

[Amit, Rahul, Priya]
Serving: Amit
[Rahul, Priya]

Important Queue Methods:

MethodDescription
add()Inserts an element
offer()Inserts an element
poll()Removes and returns the head
peek()Returns the head without removing it
remove()Removes the head or throws an exception if empty

Important Features:

  • Follows FIFO in a normal FIFO queue.
  • Useful for task processing.
  • Supports different implementations such as LinkedList and PriorityQueue.

When should we use Queue?

Use Queue when tasks or requests need to be processed in arrival order, such as ticket booking, printer jobs, and customer support requests.

6. ConcurrentHashMap – Thread-Safe Key-Value Storage

What is ConcurrentHashMap?

ConcurrentHashMap is a thread-safe implementation of the ConcurrentMap interface.

It is designed for applications where multiple threads need to access and modify a shared key-value map concurrently.

Real-world example: Multiple Users Updating Login Counters

Imagine an application where multiple threads update the login count of the same employee.

Using a normal HashMap without synchronization can cause concurrency problems.

ConcurrentHashMap provides thread-safe operations for this type of scenario.

Java Example

import java.util.concurrent.*;

public class Main {

    public static void main(String[] args) {

        ConcurrentHashMap<Integer, Integer> loginCount =
            new ConcurrentHashMap<>();

        Runnable task = () -> {

            for (int i = 0; i < 1000; i++) {

                loginCount.merge(
                    101,
                    1,
                    Integer::sum
                );
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        System.out.println(loginCount);
    }
}

Output:

{101=2000}

Important Features:

  • Supports concurrent access by multiple threads.
  • Provides thread-safe operations.
  • Does not allow null keys or null values.
  • Provides better concurrency than a synchronized HashMap for many workloads.
  • Supports atomic operations such as putIfAbsent() and merge().

When should we use ConcurrentHashMap?

Use ConcurrentHashMap when multiple threads need to read and update a shared key-value map safely.

Java Collections Quick Comparison

CollectionMain PurposeDuplicatesOrdering
ArrayListIndex-based storageAllowedInsertion order
HashSetUnique elementsNot allowedNo guaranteed order
HashMapKey-value storageUnique keysNo guaranteed order
TreeMapSorted key-value storageUnique keysSorted keys
QueueFIFO processingGenerally allowedProcessing order
ConcurrentHashMapConcurrent key-value storageUnique keysNo guaranteed order

How to Choose the Right Collection?

Remember these practical scenarios:

  1. Need duplicates and index-based access? Use ArrayList.
  2. Need unique elements? Use HashSet.
  3. Need key-value storage? Use HashMap.
  4. Need sorted keys? Use TreeMap.
  5. Need FIFO processing? Use Queue.
  6. Need thread-safe concurrent key-value access? Use ConcurrentHashMap.

Practice Assignments for Students

Try implementing the following scenarios using the appropriate Java collection.

  1. Create a shopping cart that allows duplicate products.
  2. Create a collection of unique email addresses.
  3. Store employee IDs and employee names.
  4. Store student records sorted by roll number.
  5. Implement a bank customer waiting queue.
  6. Create a login counter updated by multiple threads using ConcurrentHashMap.

Conclusion

Java Collections Framework makes it easier to manage data in Java applications.

Understanding when to use ArrayList, HashSet, HashMap, TreeMap, Queue, and ConcurrentHashMap is essential for every Java developer.

Instead of memorizing collection classes, focus on understanding their behavior, internal working, performance characteristics, and real-world use cases.