Project Based Learning in Java

Java Collections Framework

Collection hierarchy, generics, core operations, Iterable, and Iterator.

Lecture 2.1.1 CO3 aligned Unit 2 · Chapter 2.1

Learning outcome: Explain the Java Collections Framework, choose suitable collection abstractions, use generics, and traverse elements safely with enhanced for loops and iterators.

Programs frequently need to store a group of related objects: student names, product records, task IDs, or marks. Arrays can hold several values, but their length is fixed. The Java Collections Framework provides reusable interfaces, implementations, and algorithms for groups that may grow, shrink, search, sort, and change while a program runs.

What Is a Collection?

A collection is an object that groups multiple elements into a single unit so that they can be stored, accessed, and processed together.

In simple terms: A collection is a container object. Instead of declaring fifty separate variables for fifty marks, keep one container and put all fifty marks inside it. The container itself knows how to add, count, search, and remove items.

In Java, Collection<E> is the root interface for most element-based containers in the Java Collections Framework. It defines common operations such as adding, removing, searching, and traversing elements. Interfaces such as List<E>, Set<E>, and Queue<E> extend Collection<E>. Map<K,V> belongs to the framework but has a separate hierarchy because it stores key-value associations rather than individual elements.

Collection<String> names = new ArrayList<>();

Here, Collection<String> is the interface type, ArrayList<String> is the implementation, and each stored element is a String.

Why Collections Are Needed

An array is a good choice when the number of elements is known and fixed. A collection is usually better when the program needs flexible size or higher-level operations.

Requirement Array Collection
Size Fixed after creation Usually grows or shrinks dynamically
Element access Index based Depends on the interface and implementation
Ready-made operations Limited Add, remove, search, filter, sort, and more
Primitive values Stores primitives directly Stores objects; primitives use wrapper types
Type safety Declared element type Generics declare the element type

For example, an ArrayList<Integer> can store integer values through the Integer wrapper class and can expand as values are added.

In simple terms: An array is a fixed row of chairs. Once thirty chairs are set out, the thirty-first guest has nowhere to sit. A collection is a row where staff keep adding chairs as guests arrive.

Framework, Interface, and Implementation

A framework is a coordinated set of interfaces and classes designed to solve a family of problems. In the Collections Framework:

  • Interfaces describe the behaviour a data structure offers.
  • Implementations provide the actual storage strategy.
  • Algorithms perform reusable operations such as sorting or searching.
List<String> names = new ArrayList<>();

Here, List is the interface and ArrayList is the implementation. Declaring the variable with the interface type keeps the code flexible.

In simple terms: The interface is the menu: it lists what can be ordered. The implementation is the kitchen: it decides how the dish is actually prepared. Because the rest of the program only reads the menu, the kitchen can be changed from ArrayList to LinkedList without touching any other line.

Collection Hierarchy

The simplified hierarchy shows the most frequently used abstractions:

Interface Main idea Common implementations
List<E> Ordered sequence with index positions and duplicates ArrayList, LinkedList
Set<E> Unique elements HashSet, LinkedHashSet, TreeSet
Queue<E> Elements processed in a defined retrieval order ArrayDeque, PriorityQueue, LinkedList
Map<K,V> Unique keys mapped to values HashMap, LinkedHashMap, TreeMap

Iterable<E> sits above Collection<E>. It supplies iterator() and makes the enhanced for loop possible.

Collection and Collections Are Different

The names look similar but represent different ideas.

In simple terms: Collection (singular) is the thing that holds data. Collections (plural, with an s) is a toolbox of ready-made helper methods that operate on such things. One is a noun; the other is a set of tools.

Name Kind Purpose
Collection<E> Interface Root abstraction for groups such as lists, sets, and queues
Collections Utility class Static algorithms and wrappers such as sort, reverse, min, max, and unmodifiableList
List<Integer> scores = new ArrayList<>();
scores.add(72);
scores.add(91);
scores.add(84);

Collections.sort(scores);
System.out.println(scores);

Output:

[72, 84, 91]

Generics and Type Safety

Generics specify the type of element a collection can hold.

List<String> cities = new ArrayList<>();
cities.add("Chandigarh");
cities.add("Mohali");

The compiler prevents an unrelated value from being added:

// cities.add(42);  // compile-time error

Without generics, code would need manual casts and could fail at runtime. Prefer parameterized forms such as List<String> instead of raw forms such as List.

In simple terms: <String> is a label on the box reading "strings only". The compiler reads that label and rejects a wrong item immediately, so no surprise ClassCastException appears later while the program is running.

Core Collection Operations

Most collection implementations inherit a common vocabulary from Collection<E>.

Method Purpose
add(e) Adds an element when the operation is supported
addAll(c) Adds all elements from another collection
remove(o) Removes one matching element
removeIf(test) Removes every element that satisfies a condition
contains(o) Tests whether a matching element exists
size() Returns the number of elements
isEmpty() Tests whether the collection has no elements
clear() Removes all elements
retainAll(c) Keeps only elements also found in another collection
toArray() Creates an array containing the elements

Some collection objects are unmodifiable. Their update methods throw UnsupportedOperationException.

List<String> fixed = List.of("Java", "SQL", "Git");
System.out.println(fixed.contains("Java"));
// fixed.add("HTML");  // UnsupportedOperationException

Output:

true

In simple terms: List.of(...) produces a read-only list, like a printed notice board. Reading is allowed; writing is not. To edit it, first copy it with new ArrayList<>(fixed).

Iterating over a Collection

Enhanced for Loop

Use the enhanced for loop when each element only needs to be read.

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

public class CourseList {
    public static void main(String[] args) {
        List<String> courses = new ArrayList<>();
        courses.add("Java");
        courses.add("Database Systems");
        courses.add("Computer Networks");

        for (String course : courses) {
            System.out.println(course);
        }
    }
}

Output:

Java
Database Systems
Computer Networks

Iterator

An Iterator<E> provides a cursor-like way to move through a collection.

In simple terms: An iterator is a bookmark. hasNext() asks "is there another page?", next() turns to it and hands it over, and remove() tears out the page just read. Because the bookmark and the collection stay in step, this is the safe way to delete while traversing.

Method Meaning
hasNext() Returns true when another element is available
next() Returns the next element and advances the iterator
remove() Removes the last element returned by this iterator, if supported
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

public class IteratorDemo {
    public static void main(String[] args) {
        List<Integer> marks = new ArrayList<>(List.of(92, 38, 75, 29));
        Iterator<Integer> iterator = marks.iterator();

        while (iterator.hasNext()) {
            if (iterator.next() < 40) {
                iterator.remove();
            }
        }

        System.out.println(marks);
    }
}

Output:

[92, 75]

Removing through the iterator is safer than structurally changing the same collection directly during iteration.

Why not just call marks.remove(...) inside the loop? The collection would change underneath the iterator, and the next call usually throws ConcurrentModificationException. iterator.remove() keeps both in step; removeIf does the same job in one line.

Bulk Operations

Bulk methods express set-like changes clearly.

In simple terms: retainAll keeps only the items present in both collections, that is, the overlap. Here it answers the question "which enrolled students actually submitted?"

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

public class BulkOperations {
    public static void main(String[] args) {
        List<String> enrolled = new ArrayList<>(
                List.of("Aman", "Diya", "Kabir", "Meera"));
        List<String> submitted = List.of("Diya", "Meera", "Zoya");

        enrolled.retainAll(submitted);
        System.out.println(enrolled);
    }
}

Output:

[Diya, Meera]

Choosing the Right Abstraction

Ask what behaviour the program requires before choosing a class.

Question Likely choice
Must positions and duplicates be preserved? List
Must every element be unique? Set
Must values be found by a unique key? Map
Must elements wait to be processed? Queue or Deque
Is order irrelevant but fast membership testing important? Usually HashSet

Program to the most general interface that expresses the requirement.

Common Mistakes

Mistake Correction
Treating Map as a subtype of Collection Map is part of the framework but has its own hierarchy.
Using a raw collection such as List items Use a generic type such as List<String>.
Modifying a collection directly inside an iterator loop Use Iterator.remove() or a method such as removeIf.
Assuming every collection preserves insertion order Check the contract of the selected implementation.
Calling add on a collection created by List.of Copy it into a mutable list when updates are required.

Practice

  1. Create a collection of five city names and print them with an enhanced for loop.
  2. Remove every negative value from a list by using removeIf.
  3. Use retainAll to find names that appear in two lists.
  4. Explain why Map needs two type parameters while Collection needs one.

Quick Check

1. What is the root interface of the main collection hierarchy?

Collection<E> is the root interface for lists, sets, and queues. It extends Iterable<E>.

2. Does Map extend Collection?

No. Map belongs to the Collections Framework but has a separate key-value hierarchy.

3. Why are generics useful in collections?

Generics provide compile-time type safety and remove the need for most manual casts.

4. Which iterator method tests whether another element exists?

hasNext() returns true when another element is available.

Lecture Quiz

Which choice best describes Collections: an interface for groups, a utility class of static operations, a list implementation, or a map implementation?

Collections is a utility class containing static operations and wrappers for collection objects.

Summary

The Java Collections Framework combines interfaces, implementations, and algorithms for working with groups of objects. Collection is the main interface for lists, sets, and queues, while Map models key-value associations separately. Generics provide type safety, and Iterable and Iterator support controlled traversal.