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
E stands for Element. It is a generic type placeholder; for example, in Iterable<String>, E is String.Collection.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
- Create a collection of five city names and print them with an enhanced
forloop. - Remove every negative value from a list by using
removeIf. - Use
retainAllto find names that appear in two lists. - Explain why
Mapneeds two type parameters whileCollectionneeds 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.