Collections Framework

Managing Groups of Objects Efficiently

← Back to Index

What are Collections?

Imagine you need to store the names of all students in a classroom. You could create individual variables like this:

String student1 = "Alice";
String student2 = "Bob";
String student3 = "Charlie";
// ... What if you have 100 students? This is terrible!

This approach is impractical! Instead, Java provides Collections - special objects that can hold multiple items together, like a container.

List<String> students = new ArrayList<>();
students.add("Alice");
students.add("Bob");
students.add("Charlie");
// Add as many as you need!

Real-World Analogies

Why Use Collections?

The Collections Framework

The Java Collections Framework is a set of classes and interfaces that implement commonly used collection data structures. Think of it as a toolbox with different containers for different needs.

Core Interfaces - Quick Reference
  • List - Ordered collection that allows duplicates
    Example: ArrayList, LinkedList
    Use when: Order matters, need duplicates
  • Set - Unordered collection with no duplicates
    Example: HashSet, TreeSet
    Use when: Need unique items only
  • Map - Key-value pairs (no duplicate keys)
    Example: HashMap, TreeMap
    Use when: Need to look up values by key
  • Queue - Collection for holding elements before processing
    Example: LinkedList, PriorityQueue
    Use when: Need FIFO processing

Arrays vs Collections - What's the Difference?

Feature Array Collection (e.g., ArrayList)
Size Fixed (cannot change) Dynamic (grows/shrinks)
Types Can hold primitives Holds objects only (use Integer not int)
Methods Just length property Many useful methods (add, remove, contains)
Syntax String[] arr = new String[5]; List<String> list = new ArrayList<>();

Now let's explore each collection type in detail with practical examples...

List Interface

Lists maintain insertion order and allow duplicate elements. They provide positional access and search operations.

ArrayList - Dynamic Array Implementation

import java.util.*;

public class ListExample {
    public static void main(String[] args) {
        // Create an ArrayList
        List<String> fruits = new ArrayList<>();

        // Add elements
        fruits.add("Apple");
        fruits.add("Banana");
        fruits.add("Orange");
        fruits.add("Apple");  // Duplicates allowed

        // Access by index
        String first = fruits.get(0);  // "Apple"

        // Update element
        fruits.set(1, "Mango");

        // Remove element
        fruits.remove("Orange");
        fruits.remove(0);  // Remove by index

        // Check if contains
        boolean hasApple = fruits.contains("Apple");

        // Iterate
        for (String fruit : fruits) {
            System.out.println(fruit);
        }

        // Size
        int size = fruits.size();
    }
}

LinkedList - Doubly-Linked List Implementation

// Better for frequent insertions/deletions
List<Integer> numbers = new LinkedList<>();
numbers.add(1);
numbers.add(2);
numbers.add(0, 0);  // Insert at beginning - O(1)

// LinkedList also implements Queue
LinkedList<String> queue = new LinkedList<>();
queue.addFirst("First");
queue.addLast("Last");
String head = queue.removeFirst();
When to Use
  • ArrayList - Fast random access, use when you access elements by index frequently
  • LinkedList - Fast insertions/deletions, use for queue/deque operations

Set Interface

Sets contain unique elements with no duplicates. They model the mathematical set abstraction.

HashSet - Fastest, No Order Guarantee

import java.util.*;

Set<String> uniqueNames = new HashSet<>();

// Add elements
uniqueNames.add("Alice");
uniqueNames.add("Bob");
uniqueNames.add("Alice");  // Duplicate - will be ignored

System.out.println(uniqueNames.size());  // 2, not 3

// Check membership - O(1) average
if (uniqueNames.contains("Alice")) {
    System.out.println("Alice is in the set");
}

// Remove
uniqueNames.remove("Bob");

TreeSet - Sorted Order

// Elements are sorted (natural order or custom comparator)
Set<Integer> sortedNumbers = new TreeSet<>();
sortedNumbers.add(5);
sortedNumbers.add(2);
sortedNumbers.add(8);
sortedNumbers.add(1);

// Iterates in sorted order: 1, 2, 5, 8
for (int num : sortedNumbers) {
    System.out.println(num);
}

// Custom comparator
Set<String> reverseSet = new TreeSet<>(Comparator.reverseOrder());
reverseSet.add("C");
reverseSet.add("A");
reverseSet.add("B");
// Order: C, B, A

LinkedHashSet - Insertion Order Preserved

// Maintains insertion order
Set<String> orderedSet = new LinkedHashSet<>();
orderedSet.add("First");
orderedSet.add("Second");
orderedSet.add("Third");
// Iterates in insertion order
Set Comparison
Type Order Performance Use When
HashSet No order Fastest (O(1)) You don't care about order
TreeSet Sorted O(log n) You need sorted elements
LinkedHashSet Insertion order Slightly slower than HashSet You need insertion order

Map Interface

Maps store key-value pairs. Each key maps to exactly one value.

HashMap - Fast Key-Value Storage

import java.util.*;

Map<String, Integer> ages = new HashMap<>();

// Put key-value pairs
ages.put("Alice", 25);
ages.put("Bob", 30);
ages.put("Charlie", 35);

// Get value by key
int aliceAge = ages.get("Alice");  // 25

// Check if key exists
if (ages.containsKey("Bob")) {
    System.out.println("Bob's age: " + ages.get("Bob"));
}

// Get with default value
int age = ages.getOrDefault("David", 0);  // 0 if not found

// Update value
ages.put("Alice", 26);  // Overwrites existing value

// Put if absent
ages.putIfAbsent("Eve", 28);

// Remove
ages.remove("Charlie");

// Iterate over entries
for (Map.Entry<String, Integer> entry : ages.entrySet()) {
    System.out.println(entry.getKey() + ": " + entry.getValue());
}

// Iterate over keys only
for (String name : ages.keySet()) {
    System.out.println(name);
}

// Iterate over values only
for (int ageValue : ages.values()) {
    System.out.println(ageValue);
}

TreeMap - Sorted by Keys

Map<String, String> sortedMap = new TreeMap<>();
sortedMap.put("C", "Cat");
sortedMap.put("A", "Apple");
sortedMap.put("B", "Ball");
// Keys are sorted: A, B, C

LinkedHashMap - Insertion Order Preserved

Map<String, Integer> orderedMap = new LinkedHashMap<>();
orderedMap.put("First", 1);
orderedMap.put("Second", 2);
// Maintains insertion order

Practical Example - Word Frequency Counter

public static Map<String, Integer> countWords(String text) {
    Map<String, Integer> wordCount = new HashMap<>();
    String[] words = text.toLowerCase().split("\\s+");

    for (String word : words) {
        wordCount.put(word, wordCount.getOrDefault(word, 0) + 1);
    }

    return wordCount;
}

// Usage
String text = "hello world hello java world";
Map<String, Integer> counts = countWords(text);
// {hello=2, world=2, java=1}

Common Collection Operations

Sorting Collections

import java.util.*;

List<Integer> numbers = Arrays.asList(5, 2, 8, 1, 9);

// Sort in natural order
Collections.sort(numbers);
// [1, 2, 5, 8, 9]

// Sort in reverse order
Collections.sort(numbers, Collections.reverseOrder());

// Sort with custom comparator
List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
Collections.sort(names, (a, b) -> a.length() - b.length());
// Sort by length

Searching Collections

List<Integer> sortedList = Arrays.asList(1, 2, 5, 8, 9);

// Binary search (list must be sorted)
int index = Collections.binarySearch(sortedList, 5);  // 2

// Find max/min
int max = Collections.max(sortedList);  // 9
int min = Collections.min(sortedList);  // 1

Converting Between Collections

// List to Set
List<String> list = Arrays.asList("A", "B", "A");
Set<String> set = new HashSet<>(list);  // Removes duplicates

// Set to List
List<String> listFromSet = new ArrayList<>(set);

// Array to List
String[] array = {"X", "Y", "Z"};
List<String> listFromArray = Arrays.asList(array);

// List to Array
String[] arrayFromList = list.toArray(new String[0]);

Why Collections Framework Matters

Common Pitfalls

ConcurrentModificationException
// DON'T: Modify collection while iterating
List<String> list = new ArrayList<>(Arrays.asList("A", "B", "C"));
for (String s : list) {
    list.remove(s);  // THROWS EXCEPTION!
}

// DO: Use Iterator.remove() or create new collection
Iterator<String> it = list.iterator();
while (it.hasNext()) {
    it.next();
    it.remove();  // Safe
}
Null Keys and Values

HashMap allows one null key and multiple null values. TreeMap doesn't allow null keys. Always check your requirements.