Java Collections

The Java Collections Framework (JCF)

The Java Collections Framework (JCF) is a set of classes and interfaces that implement commonly reusable data structures.

It provides a unified architecture for storing and manipulating groups of objects. Before the JCF, Java had several ad-hoc classes like Array, Vector, and Hashtable, but they didn't share a common interface, making them difficult to use together. The JCF fixed this by providing a standardized set of tools.


Benefits of the Collections Framework


Hierarchy of the Collections Framework

The framework is built around a set of core interfaces. The main ones are:

!Collections Framework Diagram

Core Interfaces

  1. Collection Interface: The root of the hierarchy. It represents a group of objects, known as its elements. It defines basic methods like add(), remove(), size(), and iterator().

    • Set Interface: Extends Collection. A collection that cannot contain duplicate elements. Think of a mathematical set.
      • Common Implementations: HashSet, TreeSet, LinkedHashSet.
    • List Interface: Extends Collection. An ordered collection (also known as a sequence). Lists can contain duplicate elements. You can access elements by their integer index.
      • Common Implementations: ArrayList, LinkedList.
    • Queue Interface: Extends Collection. A collection used to hold elements prior to processing. Besides basic Collection operations, queues provide additional insertion, extraction, and inspection operations. Typically, queues order elements in a FIFO (first-in-first-out) manner.
      • Common Implementations: LinkedList, PriorityQueue.
  2. Map Interface: This interface is not a true Collection because it doesn't extend the Collection interface, but it's a core part of the framework. It maps unique keys to values.

    • Common Implementations: HashMap, TreeMap, LinkedHashMap.

Common Implementations

Here's a quick look at the most frequently used collection classes:

Interface Implementation Description
List ArrayList A resizable array. Fast for random access (get()), but slower for insertions and deletions in the middle.
List LinkedList A doubly-linked list. Fast for insertions and deletions, but slower for random access.
Set HashSet Stores elements in a hash table. Provides the best performance on average (O(1)), but makes no guarantees about iteration order.
Set TreeSet Stores elements in a sorted tree structure. Elements are ordered, but performance is slower (O(log n)).
Set LinkedHashSet A hash table and linked list implementation. Provides insertion-ordered iteration and fast (O(1)) performance.
Map HashMap A hash table implementation of the Map interface. Provides O(1) performance for get() and put(). No order is guaranteed.
Map TreeMap A tree-based implementation. Keys are sorted, but performance is O(log n).
Map LinkedHashMap A hash table and linked list implementation. Provides insertion-ordered iteration and fast (O(1)) performance.

In the next chapters, we will explore each of these interfaces and implementations in detail.


Advanced: Modern Collection Factory Methods (Java 9+)

Historically, creating and initializing a collection with values required several lines of code. Since Java 9, the framework includes convenient static factory methods like List.of(), Set.of(), and Map.of() to create collections quickly in a single line.

It is crucial to note that collections created this way are immutable (unmodifiable). If you try to add, remove, or modify elements after creation, the program will crash with an UnsupportedOperationException.

Creating Immutable Collections

import java.util.List;
import java.util.Map;
import java.util.Set;

public class Main { public static void main(String[] args) { // Create an immutable List List<String> colors = List.of("Red", "Green", "Blue"); // colors.add("Yellow"); // This would throw an exception! // Create an immutable Set Set<Integer> primeNumbers = Set.of(2, 3, 5, 7, 11); // Create an immutable Map (Key1, Value1, Key2, Value2, ...) Map<String, String> capitals = Map.of( "USA", "Washington D.C.", "France", "Paris", "Japan", "Tokyo" ); System.out.println("Colors: " + colors); System.out.println("Primes: " + primeNumbers); System.out.println("Capitals Map: " + capitals); } }


Advanced: Sorting Custom Objects

When you use collections that maintain a sorted order (like TreeSet or TreeMap), or when you use sorting algorithms on a List, Java needs to know how to compare the objects. For built-in types like String or Integer, Java already knows. But for your custom classes, you must define the sorting rules.

The most common way to do this is by having your custom class implement the Comparable interface and overriding the compareTo() method to define its "natural" default sorting order.

Using Comparable to Sort Objects

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

// Implement Comparable and specify the type to compare against class Student implements Comparable<Student> { String name; int grade;

public Student(String name, int grade) { this.name = name; this.grade = grade; }

@Override public int compareTo(Student otherStudent) { // Sort by grade in descending order (highest first) return Integer.compare(otherStudent.grade, this.grade); }

@Override public String toString() { return name + " (" + grade + ")"; } }

public class Main { public static void main(String[] args) { List<Student> students = new ArrayList<>(); students.add(new Student("Alice", 85)); students.add(new Student("Bob", 92)); students.add(new Student("Charlie", 78)); // Collections.sort automatically uses the compareTo method we wrote! Collections.sort(students); System.out.println(students); // Outputs: [Bob (92), Alice (85), Charlie (78)] } }


Exercise

?

Which interface maps unique keys to values and is NOT a true Collection?