Appearance
Java Collections Framework: Hierarchy, Iteration, and Core Methods
Java is full of moments where things just click. The Java Collections Framework is one of those moments. Once you understand why it was built, every class and interface inside it stops feeling like memorization and starts feeling like an obvious design decision. This article walks you through the entire foundation: what a collection is, why the framework exists, how the hierarchy is structured, all three ways to iterate, the common methods every collection shares, and the difference between
Collection and Collections. By the end you will have a mental model that makes every future deep dive into individual classes much easier.
What Is a Collection?
Before any framework, before any hierarchy, there is a simple idea. A collection is just a group of objects, also called a group of elements. When you create an array and fill it with values, that array is already a collection in the plain English sense of the word.
java
int[] a = {1, 2, 3, 4}; // a group of integers — a collection in conceptEvery collection in Java lives in the java.util package. That is the home of all the classes and interfaces you will work with: List, ArrayList, LinkedList, Stack, Queue, Set, Map, and everything else. They were all brought together into one organized system so that programmers would have a single, coherent place to look.
A framework is the architecture that holds all of these pieces together. Java provides you with already written classes, interfaces, and methods so you never have to build a resizable list or a hash based lookup from scratch. You can use what is there, and if you need something special on top, the framework is open enough for you to extend it. That combination, ready built functionality plus extensibility, is what makes something a framework rather than just a library.
Why the Collections Framework Was Necessary
The Collections Framework was added in Java version 1.2. That is not a trivia fact. It is the answer to a question that almost every interviewer asks: why do we need this framework at all?
Before Java 1.2 there were already ways to hold groups of objects. You had arrays. You had Vector. You had Hashtable. These things worked, but they shared one serious problem: there was no common interface between them.
Think about what that means in practice. Suppose you have three different kinds of collections in your program.
java
// Array: you write to it and read from it like this
int[] arr = new int[4];
arr[0] = 1; // write
int val = arr[0]; // read
// Vector: totally different syntax
Vector<Integer> vec = new Vector<>();
vec.add(1); // write
int val2 = vec.get(0); // read
// Hashtable: yet another way
Hashtable<String, Integer> table = new Hashtable<>();
table.put("key", 1); // write
int val3 = table.get("key"); // readEvery single collection has a different method name for doing the same conceptual operation. Inserting an element is arr[i] = value for arrays, .add() for Vector, and .put() for Hashtable. Reading an element is index syntax for arrays, .get(index) for Vector, and .get(key) for Hashtable. There is no shared vocabulary. Every time you switch to a different kind of collection, you have to relearn how to talk to it.
That is the root problem the Collections Framework solved: there was no common interface. The framework introduced a hierarchy of interfaces so that no matter which concrete collection you pick, you always talk to it using the same set of method names. You never have to remember that ArrayList uses .add() but some other class uses .insert() because they all use .add(). The interface enforces it.
If an interviewer asks you why the Collections Framework was introduced, that is your answer: the pre framework world had multiple collections with no common interface, making it hard to remember how to read and write each one. The framework unified them under a single hierarchy so programmers could focus on choosing the right data structure for their use case rather than memorizing different method names for every type.
The Hierarchy: Two Trees, Not One
Here is something that surprises a lot of people when they first look at the hierarchy diagram. The Collections Framework is actually two separate trees, not one.
The first tree has Iterable at the very top. Below Iterable sits Collection. Below Collection split into three branches: List, Queue, and Set. Each of those interfaces has concrete classes hanging below it. List has ArrayList, LinkedList, Vector, and Stack. Queue has PriorityQueue, ArrayDeque, and LinkedList (which implements both List and Queue). Set has HashSet, LinkedHashSet, and TreeSet.
The second tree is completely separate. Map sits on its own, not under Iterable, not under Collection. It is its own root. HashMap, LinkedHashMap, TreeMap, Hashtable, and others hang below it.
In the visual that is often shown when teaching this topic, the first tree is everything on the left side, and Map stands alone on the right side. The reason for that separation is important and will come up in interviews, so we will address it fully in the next lecture. For now, understand the shape: one family under Iterable, and Map standing independently.
Understanding Iterable: The Root of the First Tree
Iterable is the topmost interface of the main collection family. Its one job is to allow traversal. Anything that implements Iterable is saying: you can loop over me.
Here is a timeline fact that matters: Iterable was added in Java 1.5. The Collection interface and all the concrete classes below it were added in Java 1.2. So for three years, before Iterable existed, Java already had ArrayList, Vector, Stack, and the rest of them. How did traversal work back then? Every collection already had an iterator() method built directly into the Collection interface. The Iterator object itself has been in Java since 1.2. When Java 1.5 came along, the designers pulled the iteration methods out into their own dedicated interface, Iterable, so that traversal could be defined cleanly at the top of the hierarchy. They also wanted to support the enhanced for loop, which required that objects declare themselves as iterable. So Iterable is less of a new feature and more of a formalization of something that already existed, plus the foundation for the for each loop syntax.
Iterable exposes two main methods you need to know:
The iterator() method, which has been available since Java 1.5 in the Iterable interface (though the underlying Iterator object existed since 1.2), returns an Iterator object you can use to walk through the collection step by step.
The forEach() method was added in Java 1.8. It takes a functional interface as its parameter and applies a lambda expression to each element in the collection.
Three Ways to Iterate a Collection
Because Iterable sits at the top of the hierarchy, every concrete class that descends from it (every ArrayList, every Stack, every PriorityQueue, and so on) supports all three iteration approaches. Learn these once and you know them for every collection.
The Iterator Object
The first and most explicit approach uses the Iterator object directly. You call .iterator() on your collection, which returns an Iterator. That object has three methods:
hasNext()returnstrueif there are more elements remaining to visit.next()returns the next element and advances the cursor.remove()removes the last element returned bynext()from the underlying collection.
Here is a complete example showing all three methods in action:
java
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class IteratorDemo {
public static void main(String[] args) {
// Create a list with four elements
List<Integer> values = new ArrayList<>();
values.add(1);
values.add(2);
values.add(3);
values.add(4);
// Get the iterator object
Iterator<Integer> iterator = values.iterator();
// Walk through the collection
while (iterator.hasNext()) { // are there more elements?
int value = iterator.next(); // get the next element
System.out.println(value);
if (value == 3) {
iterator.remove(); // remove element 3 from the list
}
}
// Verify that 3 was removed by printing the list
System.out.println("After removal:");
for (int v : values) {
System.out.println(v); // prints 1, 2, 4
}
}
}Walk through this mentally. The iterator starts before the first element. The first call to hasNext() returns true because there are four elements waiting. next() returns 1 and moves the cursor. The loop continues, returning 2, then 3. When value == 3, we call iterator.remove(), which removes 3 from the underlying list. On the final pass, next() returns 4. After the loop, the list contains 1, 2, and 4.
This is the most powerful form of iteration because remove() lets you safely delete elements while walking through the collection. Trying to remove elements during a normal for loop or enhanced for loop will throw a ConcurrentModificationException. The iterator is the safe way to modify while iterating.
The Enhanced For Loop
The second approach is the familiar enhanced for loop, also called the for each loop. This is simpler to write and covers the vast majority of cases where you just want to read each element.
java
List<Integer> values = new ArrayList<>();
values.add(1);
values.add(2);
values.add(3);
values.add(4);
// Enhanced for loop — clean and simple
for (int value : values) {
System.out.println(value);
}The reason this works on any collection in the first family is directly because of Iterable. The Java language specification says that any object that implements Iterable can be used as the target of a for each loop. That was the whole point of adding Iterable in Java 1.5: to give the compiler a contract it could rely on to generate the for each loop's bytecode. Internally, the compiler turns your enhanced for loop into iterator calls. It is syntactic sugar over the first approach.
The forEach Method with Lambda
The third approach is the forEach() method that was added to Iterable in Java 1.8. It accepts a Consumer, which is one of Java's built in functional interfaces. Because Consumer is a functional interface (it has exactly one abstract method), you can pass a lambda expression instead of creating an anonymous class.
java
List<Integer> values = new ArrayList<>();
values.add(1);
values.add(2);
values.add(4); // 3 was removed earlier in our example
// forEach with a lambda expression
values.forEach(value -> System.out.println(value));
// Or using a method reference, which is even more concise
values.forEach(System.out::println);What happens internally is that forEach loops over every element in the collection and calls the lambda expression once for each one, passing the current element as the argument. The Consumer interface's abstract method accepts one parameter and returns nothing, which is exactly what value -> System.out.println(value) does.
This approach became popular in Java 1.8 alongside streams because it makes collection processing feel more declarative. Instead of saying "walk through this, check if there are more, get the next one," you say "for every element in this, do this thing."
Quick Timeline Recap
Just to nail the history in your mind:
The Iterator object has existed since Java 1.2, living inside the Collection interface. Every concrete collection had an iterator() method even before Iterable existed.
The Iterable interface was formalized in Java 1.5. That is when the enhanced for loop also became available, because the for each loop requires Iterable.
The forEach() method that accepts a lambda was added in Java 1.8 along with the rest of the functional programming additions.
The Collection Interface: Common Ground for All
Below Iterable in the hierarchy sits Collection. While Iterable is about traversal, Collection is about data operations. It is the interface that exposes all the methods you will use to add, remove, search, and otherwise manipulate a group of objects.
Every concrete class in the main family, ArrayList, LinkedList, Stack, PriorityQueue, HashSet, all of them, either implements Collection directly or implements an interface that extends Collection. This is the mechanism that gives every collection the same vocabulary.
Here are the most commonly used methods from Collection, all available since Java 1.2 unless otherwise noted:
size() returns the total number of elements currently in the collection. If you have {2, 3, 4} in your list, size() returns 3.
isEmpty() returns true if the collection has no elements. It is a convenience method equivalent to checking size() == 0.
contains(Object o) searches the collection for the given object and returns true if it is present, false otherwise.
toArray() converts the collection to a plain Java array. Useful when you need to pass collection data to code that expects an array.
add(E e) inserts an element into the collection. For ordered collections like ArrayList it goes at the end. For sets it goes in wherever the data structure places it.
remove(Object o) removes the first occurrence of the specified object from the collection.
addAll(Collection c) takes another collection and inserts all of its elements into the current collection.
removeAll(Collection c) removes from the current collection every element that also appears in the collection passed as a parameter.
containsAll(Collection c) returns true only if every element in the given collection is also present in the current collection.
clear() removes every element from the collection, leaving it empty.
equals(Object o) checks whether two collections are equal.
stream() and parallelStream() were added in Java 1.8. They return a Stream object that lets you apply functional style operations like filtering, mapping, and reducing. Streams are powerful enough to deserve their own dedicated coverage.
iterator() returns an Iterator as we already discussed.
Seeing All the Methods Together
Here is a single code example that exercises every major method:
java
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;
public class CollectionMethodsDemo {
public static void main(String[] args) {
// Create a list and add three elements
List<Integer> values = new ArrayList<>();
values.add(2);
values.add(3);
values.add(4);
// size
System.out.println(values.size()); // 3
// isEmpty
System.out.println(values.isEmpty()); // false
// contains
System.out.println(values.contains(5)); // false
// add
values.add(5);
System.out.println(values.contains(5)); // true
// remove by index (primitive int = index)
values.remove(3); // removes element at index 3, which is 5
System.out.println(values.contains(5)); // false
// remove by object (Integer wrapper = value search)
values.remove(Integer.valueOf(3)); // removes the value 3
System.out.println(values.contains(3)); // false
// values is now {2, 4}
// addAll
Stack<Integer> stackValues = new Stack<>();
stackValues.add(6);
stackValues.add(7);
stackValues.add(8);
values.addAll(stackValues);
// values is now {2, 4, 6, 7, 8}
// containsAll
System.out.println(values.containsAll(stackValues)); // true — 6,7,8 are all present
// remove one element so containsAll fails
values.remove(Integer.valueOf(7));
System.out.println(values.containsAll(stackValues)); // false — 7 is gone
// removeAll
values.removeAll(stackValues); // removes 6 and 8 (7 already gone)
System.out.println(values.contains(8)); // false
// clear
values.clear();
System.out.println(values.isEmpty()); // true
}
}Notice the remove gotcha. When you call values.remove(3) with a primitive int, Java treats 3 as an index and removes the element at position three. When you call values.remove(Integer.valueOf(3)), Java sees an object reference and searches for the value 3 to remove. This distinction between removing by index and removing by value is a classic pitfall. Always wrap the value in Integer.valueOf() when you mean to remove by content, not by position.
Also notice that addAll used a Stack to add elements into an ArrayList. This works because both implement Collection. The addAll method accepts any Collection, regardless of the concrete type. This is the common interface doing its job: you do not need different versions of addAll for different input types.
Collection vs Collections: A Common Interview Trap
One of the most frequently asked interview questions about this topic sounds almost like a trick: what is the difference between Collection and Collections?
Collection (no s) is an interface. It is part of the Collections Framework hierarchy. It extends Iterable. It declares all the methods like add, remove, contains, size, and so on. All the concrete collection classes implement it directly or indirectly. It is the shared contract that gives every collection its common vocabulary.
Collections (with an s) is a utility class. It is a regular class, not an interface. Every method inside it is static, which is the hallmark of a utility class. You never create an instance of Collections. You just call its static methods directly using the class name.
java
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class CollectionsUtilityDemo {
public static void main(String[] args) {
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(3);
list.add(2);
list.add(4);
// list is {1, 3, 2, 4}
// static methods called on the class, not an instance
System.out.println(Collections.max(list)); // 4
System.out.println(Collections.min(list)); // 1
Collections.sort(list);
// list is now {1, 2, 3, 4}
list.forEach(System.out::println); // prints 1 2 3 4
}
}The utility methods available in Collections include sort, binarySearch, reverse, swap, copy, min, max, rotate, shuffle, and more. These are convenience operations. You could write your own sort algorithm, but Collections.sort is already there and already tested. That is what makes it a utility: it makes your work easier without being strictly required.
Both Collection and Collections are part of the Collections Framework and both have been available since Java 1.2. The distinction is purely conceptual: one is the interface that defines what a collection is, the other is a toolbox of static helper methods that operate on collections.
If you get this question in an interview, state it clearly: Collection is an interface in the hierarchy, Collections is a utility class with only static methods, and they are unrelated to each other in the inheritance sense.
How the Hierarchy Solves the Pre Framework Problem
Let us tie everything together and fully close the loop on why this framework matters.
Before Java 1.2, if you needed to switch from using a Vector to a Hashtable in your code, you had to find every place you were reading and writing the collection and rewrite those lines with the correct method names for the new type. There was no way to write code that worked generically across different collection types.
After Java 1.2, you write your code against the interface.
java
// Write against the Collection interface
Collection<Integer> values = new ArrayList<>(); // implementation
values.add(1);
values.add(2);
values.add(3);
// Later you can swap ArrayList for Stack with zero change to the rest of the code
Collection<Integer> values = new Stack<>();
values.add(1);
values.add(2);
values.add(3);All the add, remove, contains, and other method calls stay exactly the same because both ArrayList and Stack implement Collection. The only line that changes is the one where you create the object. Every line that uses the object stays untouched.
This is the single most important architectural benefit of the framework. You focus your thinking on the right question: which collection fits my use case? You do not waste mental energy memorizing which method name does what for each individual class. Pick the right structure, and the interface takes care of the rest.
Map: The Separate Family
One last structural point that will get its own full treatment in the next lecture, but that you should understand now: Map is not part of the Iterable or Collection family.
Map stores data as key value pairs. That fundamental difference in structure means it cannot fit cleanly into the Collection hierarchy. A Collection represents a group of individual elements. A Map represents a group of relationships between two objects, a key and a value. When you iterate a Collection, you get elements. When you iterate a Map, what do you get? Keys? Values? Both? The concept does not translate cleanly.
That is why Map has its own separate root in the hierarchy. It stands apart from Iterable, and from Collection. It has its own iteration mechanisms and its own set of methods. HashMap, LinkedHashMap, TreeMap, and Hashtable all fall under this separate tree.
Interview Questions from This Topic
Here are the questions that come directly from what we covered, the ones most likely to appear:
Why was the Java Collections Framework introduced? Before Java 1.2, Java had arrays, Vector, and Hashtable but no common interface between them. Every collection had different method names for the same operations. The framework introduced a unified hierarchy so all collections share the same interface and the same method names.
What is the difference between Collection and Collections? Collection is an interface in the hierarchy. Collections is a utility class containing only static helper methods like sort, min, max, and reverse.
When was Iterable added to Java? Java 1.5. The Iterator object itself existed since Java 1.2 inside the Collection interface. Iterable was formalized as its own interface in 1.5 to enable the enhanced for loop.
What are the three ways to iterate a collection? Using the Iterator object with hasNext() and next(). Using the enhanced for loop. Using the forEach() method with a lambda, available since Java 1.8.
Why is Map not under Collection or Iterable? A Map stores key value pairs rather than individual elements, so it does not fit the element based model of Collection. It has its own separate hierarchy.
How do you safely remove elements while iterating? Use the Iterator object directly and call iterator.remove(). Using a regular for loop or enhanced for loop while removing elements throws ConcurrentModificationException.
What is the difference between remove(int index) and remove(Object o) on a List? remove(int index) removes the element at that position. remove(Object o) searches for that value and removes its first occurrence. To remove by value when working with Integer elements, use remove(Integer.valueOf(n)) so Java treats the argument as an object, not an index.
What is the parent interface of all collections? Iterable is the root of the main collection family. Collection extends Iterable and is the parent of List, Queue, and Set.
What Comes Next
This lecture laid the foundation. You now understand why the framework exists, how the hierarchy is organized, all three ways to iterate, the full set of common Collection methods, and the important distinction between Collection and Collections.
In the upcoming lectures we will go deeper into each concrete class: ArrayList, LinkedList, Stack, PriorityQueue, ArrayDeque, HashSet, LinkedHashSet, TreeSet, and the entire Map family. For each one you will learn what makes it unique, when to choose it over the others, and how its internal implementation shapes its performance characteristics. We will also cover why Map stands apart from the main hierarchy with a full explanation.
Then comes streams, which deserves its own dedicated lecture because it builds on everything in this one and unlocks an entirely different way of thinking about collection processing. Streams are one of the most frequently tested topics in Java interviews and one of the most practically useful tools in the language. Understand this foundation first, and streams will make complete sense when we get there.