00:00

Java Collection Framework – Overview with Examples

Last updated: 11 min read

The Java Collections Framework is a set of interfaces and classes in java.util for storing and working with groups of objects. Instead of writing your own dynamic arrays, linked lists, hash tables and trees, you use ready-made, well-tested classes such as ArrayList, HashSet and HashMap.

It was introduced in JDK 1.2 and is used in almost every Java program. This overview explains the main interfaces, how they fit together, how to choose the right collection, and the helper tools that come with the framework.

1. What Is the Collections Framework?

A collection is an object that holds other objects, like a shopping bag holds groceries. A framework is a set of pieces designed to work together. The Collections Framework gives you three kinds of pieces:

  • Interfaces such as List, Set, Queue and Map, which describe what a collection can do.
  • Implementations such as ArrayList, HashSet and HashMap, which decide how the data is stored.
  • Algorithms in the Collections class, such as sorting, searching and shuffling, which work on any collection.

Benefits:

  • Less code: common data structures are already written.
  • Better performance: the implementations are carefully tuned.
  • Interoperability: any method that accepts a List works with every list type.
  • Type safety: with generics, the compiler checks what goes into a collection.

2. The Collection Hierarchy

There are two separate root interfaces. Collection holds single elements and is the parent of List, Set and Queue. Map holds key–value pairs and does not extend Collection, but it is still part of the framework.

Java Collection Framework hierarchy Map interface hierarchy in Java
Simplified hierarchy
Iterable
 └─ Collection
     ├─ List       → ArrayList, LinkedList, Vector → Stack
     ├─ Set        → HashSet → LinkedHashSet
     │   └─ SortedSet → NavigableSet → TreeSet
     └─ Queue      → PriorityQueue
         └─ Deque  → ArrayDeque, LinkedList

Map               → HashMap → LinkedHashMap, Hashtable, WeakHashMap, IdentityHashMap
 └─ SortedMap → NavigableMap → TreeMap

3. The Core Interfaces

InterfaceStoresDuplicatesOrderMain classes
ListElements with indexesYesInsertion orderArrayList, LinkedList
SetUnique elementsNoDepends on the classHashSet, LinkedHashSet, TreeSet
QueueElements waiting to be processedYesFIFO or priorityArrayDeque, PriorityQueue
DequeElements added and removed at both endsYesInsertion orderArrayDeque, LinkedList
MapKey–value pairsKeys no, values yesDepends on the classHashMap, LinkedHashMap, TreeMap

Here is each one in action:

CollectionsTour.java
import java.util.*;

public class CollectionsTour {
    public static void main(String[] args) {
        // List: ordered, duplicates allowed, access by index
        List<String> list = new ArrayList<>(List.of("tea", "coffee", "tea"));
        System.out.println("List:  " + list + "  get(1)=" + list.get(1));

        // Set: unique elements only
        Set<String> set = new HashSet<>(list);
        System.out.println("Set:   " + set.size() + " unique drinks");

        // Queue: first in, first out
        Queue<String> queue = new ArrayDeque<>(List.of("order-1", "order-2"));
        System.out.println("Queue: next is " + queue.poll() + ", then " + queue.peek());

        // Map: key -> value pairs (not a Collection, but part of the framework)
        Map<String, Integer> prices = new TreeMap<>(Map.of("tea", 20, "coffee", 35));
        System.out.println("Map:   " + prices + "  price of tea=" + prices.get("tea"));
    }
}
Output
List:  [tea, coffee, tea]  get(1)=coffee
Set:   2 unique drinks
Queue: next is order-1, then order-2
Map:   {coffee=35, tea=20}  price of tea=20

4. Methods Shared by Every Collection

The Collection interface defines methods that every list, set and queue has:

MethodDescription
add(e), addAll(c)Add one element or a whole collection.
remove(o), removeAll(c), removeIf(p)Remove one element, every element in another collection, or every match.
retainAll(c)Keep only the elements that are also in another collection.
contains(o), containsAll(c)Check whether elements are present.
size(), isEmpty(), clear()Count, check and empty.
iterator(), forEach(action)Visit every element.
stream(), parallelStream()Process elements with the Stream API.
toArray(), toArray(T[])Copy the elements into an array.

5. How Ordering Differs Between Classes

Choosing a class often comes down to the order you need. The same input gives different results:

OrderComparison.java
import java.util.*;

public class OrderComparison {
    public static void main(String[] args) {
        List<String> input = List.of("mango", "apple", "kiwi", "banana", "apple");

        System.out.println("ArrayList:     " + new ArrayList<>(input));
        System.out.println("LinkedHashSet: " + new LinkedHashSet<>(input));
        System.out.println("TreeSet:       " + new TreeSet<>(input));

        Map<String, Integer> lengths = new LinkedHashMap<>();
        for (String s : input) lengths.put(s, s.length());
        System.out.println("LinkedHashMap: " + lengths);
        System.out.println("TreeMap:       " + new TreeMap<>(lengths));
    }
}
Output
ArrayList:     [mango, apple, kiwi, banana, apple]
LinkedHashSet: [mango, apple, kiwi, banana]
TreeSet:       [apple, banana, kiwi, mango]
LinkedHashMap: {mango=5, apple=5, kiwi=4, banana=6}
TreeMap:       {apple=5, banana=6, kiwi=4, mango=5}
  • Hash classes (HashSet, HashMap): no guaranteed order, fastest.
  • Linked hash classes: insertion order, almost as fast.
  • Tree classes: sorted order, a little slower (O(log n)).

6. Choosing the Right Collection

You need to…Use
Keep items in order and read by positionArrayList
Keep only unique items, order does not matterHashSet
Keep unique items in insertion orderLinkedHashSet
Keep unique items sortedTreeSet
Look up a value by a keyHashMap
Keep keys in insertion order (or build an LRU cache)LinkedHashMap
Keep keys sorted, or find the nearest keyTreeMap
Process items first in, first out, or as a stackArrayDeque
Always take the smallest (or highest-priority) itemPriorityQueue
Share data between threadsConcurrentHashMap, CopyOnWriteArrayList, BlockingQueue

7. Performance at a Glance

ClassAddFind / containsRemoveGet by index
ArrayListO(1)*O(n)O(n)O(1)
LinkedListO(1) at endsO(n)O(1) at endsO(n)
HashSet / HashMapO(1)O(1)O(1)–
TreeSet / TreeMapO(log n)O(log n)O(log n)–
ArrayDequeO(1)* at endsO(n)O(1) at ends–
PriorityQueueO(log n)O(n)O(log n) for the head–

* Amortized: usually constant, with an occasional resize. Hash-based times assume a good hashCode().

8. The Collections Utility Class

java.util.Collections (note the "s") is a class full of static helper methods. Do not confuse it with the Collection interface.

CollectionsUtility.java
import java.util.*;

public class CollectionsUtility {
    public static void main(String[] args) {
        List<Integer> scores = new ArrayList<>(List.of(55, 90, 72, 90, 38));

        System.out.println("max:        " + Collections.max(scores));
        System.out.println("min:        " + Collections.min(scores));
        System.out.println("frequency:  " + Collections.frequency(scores, 90));

        Collections.sort(scores);
        System.out.println("sorted:     " + scores);
        System.out.println("binarySearch(72): index " + Collections.binarySearch(scores, 72));

        Collections.reverse(scores);
        System.out.println("reversed:   " + scores);

        Collections.swap(scores, 0, scores.size() - 1);
        System.out.println("swapped:    " + scores);

        System.out.println("nCopies:    " + Collections.nCopies(3, "hi"));

        List<Integer> readOnly = Collections.unmodifiableList(scores);
        try {
            readOnly.add(1);
        } catch (UnsupportedOperationException e) {
            System.out.println("readOnly.add(): UnsupportedOperationException");
        }
    }
}
Output
max:        90
min:        38
frequency:  2
sorted:     [38, 55, 72, 90, 90]
binarySearch(72): index 2
reversed:   [90, 90, 72, 55, 38]
swapped:    [38, 90, 72, 55, 90]
nCopies:    [hi, hi, hi]
readOnly.add(): UnsupportedOperationException

Other useful helpers include shuffle(), fill(), emptyList(), singletonList(), synchronizedList() and synchronizedMap(). The Arrays class offers similar helpers for arrays.

9. Generics and Type Safety

Before Java 5, collections held plain Object references, so you could put anything in and had to cast when taking things out. Generics let you declare the element type, and the compiler enforces it.

WhyGenerics.java
import java.util.ArrayList;
import java.util.List;

public class WhyGenerics {
    @SuppressWarnings({"rawtypes", "unchecked"})
    public static void main(String[] args) {
        // Without generics: anything can go in, errors appear at runtime
        List raw = new ArrayList();
        raw.add("hello");
        raw.add(42);                         // compiles fine
        try {
            for (Object o : raw) {
                String s = (String) o;       // fails for 42
                System.out.println(s.toUpperCase());
            }
        } catch (ClassCastException e) {
            System.out.println("Runtime error: ClassCastException");
        }

        // With generics: the compiler stops wrong types
        List<String> safe = new ArrayList<>();
        safe.add("hello");
        // safe.add(42);                     // compile error: int cannot be converted to String
        for (String s : safe) {
            System.out.println(s.toUpperCase());   // no cast needed
        }
    }
}
Output
HELLO
Runtime error: ClassCastException
HELLO

Collections can only hold objects. Primitive values such as int are automatically wrapped in Integer objects (autoboxing) when you add them.

10. Collections and Streams

Since Java 8, every collection can produce a Stream, which lets you filter, transform and group data in a few readable lines.

StreamsWithCollections.java
import java.util.*;
import java.util.stream.Collectors;

public class StreamsWithCollections {
    record Employee(String name, String dept, int salary) { }

    public static void main(String[] args) {
        List<Employee> staff = List.of(
                new Employee("Asha", "IT", 90000),
                new Employee("Ravi", "HR", 50000),
                new Employee("Meera", "IT", 75000),
                new Employee("John", "Sales", 60000));

        List<String> highEarners = staff.stream()
                .filter(e -> e.salary() > 60000)
                .map(Employee::name)
                .toList();
        System.out.println("Salary > 60000: " + highEarners);

        Map<String, List<String>> byDept = staff.stream()
                .collect(Collectors.groupingBy(Employee::dept, TreeMap::new,
                        Collectors.mapping(Employee::name, Collectors.toList())));
        System.out.println("By department:  " + byDept);

        int total = staff.stream().mapToInt(Employee::salary).sum();
        System.out.println("Total salary:   " + total);
    }
}
Output
Salary > 60000: [Asha, Meera]
By department:  {HR=[Ravi], IT=[Asha, Meera], Sales=[John]}
Total salary:   275000

11. Ways to Iterate

  • Enhanced for loop: for (String s : list) { ... }, the simplest way to read.
  • Iterator: needed when you remove elements while looping.
  • forEach() with a lambda: list.forEach(System.out::println);
  • Streams: for filtering, mapping and collecting.
  • Maps: loop over map.entrySet(), keySet() or values(), or use map.forEach((k, v) -> ...).
Remember: most collection iterators are fail-fast. Changing a collection structurally inside a for-each loop throws ConcurrentModificationException.

12. Thread-Safe Collections

The common classes (ArrayList, HashMap, HashSet and so on) are not thread-safe. For shared data, choose from java.util.concurrent:

  • ConcurrentHashMap: a high-performance thread-safe map.
  • CopyOnWriteArrayList and CopyOnWriteArraySet: for data that is read often and changed rarely.
  • ConcurrentLinkedQueue and ConcurrentLinkedDeque: non-blocking queues.
  • ArrayBlockingQueue and LinkedBlockingQueue: queues where consumers wait for new items.
  • Collections.synchronizedList(), synchronizedMap() and so on: wrappers that lock every method.

The legacy classes Vector, Stack and Hashtable are synchronized too, but are rarely used in new code.

13. Best Practices

  • Declare variables with interface types: List, Set, Map.
  • Always use generics; never use raw types.
  • Override both equals() and hashCode() in classes you put in hash-based collections.
  • Use immutable collections (List.of(), Set.of(), Map.of()) for fixed data.
  • Return empty collections instead of null.
  • Set an initial capacity when you know the size of a large ArrayList or HashMap.
  • Pick the collection by the operation you do most: lookup, ordering, uniqueness or position.

14. Summary

  • The Collections Framework provides interfaces, implementations and algorithms for groups of objects.
  • Collection is the root of List, Set and Queue; Map is a separate root for key–value pairs.
  • Hash classes are fastest, linked hash classes keep insertion order, tree classes keep sorted order.
  • The Collections class offers ready-made algorithms such as sort, max and reverse.
  • Use generics for type safety and java.util.concurrent for thread-safe collections.

Related Tutorials

Reference: Oracle: The Java Collections Framework