In this chapter, you’ll learn about Java’s Collections Framework: a set of flexible data structures that go beyond the fixed-size arrays you used in Week 1’s Arrays. Instead of being limited to one fixed-length array, you’ll work with lists, sets, and maps. Each one solves a different kind of data problem.
Almost every backend endpoint you build will involve collections: returning a list of users, looking up a product by ID, or keeping a set of unique tags. Mastering these structures now prepares you for the REST APIs you’ll build in Week 4.
Prerequisites:
The Collections Framework provides common data structures as interfaces and classes in the standard JDK.
<aside> 💡
The JDK is Java’s development kit. It includes the compiler, the runtime tools, and the source code for the Java standard library classes and interfaces.
</aside>
The most frequently used collection implementations in backend work are ArrayList, HashSet, and HashMap. Each one is connected to interfaces above it in the hierarchy. To understand why they behave differently, we first need to look at those interfaces.

Diagram of ArrayList, HashSet, and HashMap ancestor interfaces.
In the diagram above, Iterable sits near the top with very general functionality. As you move down the hierarchy, interfaces and classes become more specific and provide more behavior.
This hierarchy gives you two practical advantages:
List, you can often switch between implementations without rewriting the rest of the code.We’ll start at the top with Iterable. Then we’ll move to Collection, List, Set, and their implementations: ArrayList and HashSet. After that, we’ll look at Map and HashMap.
Iterable is an interface for objects that can be used in a for-each loop. You learned for-each loops in Control Flow.
For a class to implement Iterable, it must provide an iterator() method. The other methods in the Iterable interface have default implementations.

Iterable and its only method without default implementation: iterator().
The iterator() method returns an Iterator. An Iterator is another interface. Its most important methods are hasNext() and next():

Iterator’s forEachRemaining() and remove() methods are left out here because they are not required to implement.
Combining the method requirements of Iterable with the requirements of Iterator, we can say that for making a class work with for-each loops, in total we require 2 methods:
next(): To get the next element.hasNext(): To stop the loop when there’s no next element.That is the minimum needed for looping, so it makes sense that Iterable is high in the hierarchy.
Iterable is very general and is rarely implemented directly in everyday application code. More often, you work with interfaces that extend it, such as Collection.
Collection extends Iterable with methods such as contains(), add(), remove(), size(), and isEmpty().
<aside> ⌨️
Hands-on: Find the Collection interface in your Java installation.
First, locate your Java/JDK installation directory:
Take a look at the interface. Do not worry about understanding everything. Explore it and write down questions to ask.
There is also an easier route in IntelliJ IDEA: use Search Everywhere and search for Collection. You rarely need to navigate to the file manually, but knowing that the JDK source is available helps you understand what your IDE is showing you.
</aside>
Collection is still a general interface. Its two main subinterfacesare List and Set.
A List is an ordered collection that allows duplicate elements. Ordered means the list keeps track of element positions. You can retrieve, add, update, or remove elements by index.
<aside> ❗
Do not confuse ordered with sorted. Ordered means the collection remembers element positions. Sorted means the elements are arranged according to a rule, such as alphabetical order or numeric order.
</aside>
The main implementations are ArrayList and LinkedList. We will focus on ArrayList because it is the better default choice for most backend work.
<aside> 💡
LinkedList and ArrayList offer more-or-less the same functionality but are applied to different problems because of how they perform:
| Operation | ArrayList | LinkedList |
|---|---|---|
| Random access by index | O(1) |
O(n) |
| Add/remove at the end | Amortized O(1) (occasional resize) |
O(1) |
| Insert/delete in the middle | O(n) (shifts all following elements) |
O(n) (traverse to find middle) |
| Add/remove at the beginning | O(n) (shifts all following elements) |
O(1) |
An ArrayList is like a numbered row of seats: finding seat 50 is fast because Java can jump directly to that index, but adding a seat in the middle or the beginning means all the following seats have to be shifted.
A LinkedList is more like a webpage’s chain of links: adding or removing near a known link can be efficient, but finding the 50th link means walking through the chain from the start.
</aside>
ArrayList is a common implementation of the List interface. It is a complete class, so you can instantiate it and use it directly.
<aside> 💡
IO.println is a utility shorthand that works like System.out.println(). We use it throughout these examples for brevity. If your project does not have an IO class, use System.out.println() instead.
</aside>
Initializing an ArrayList and adding elements:
void main() {
List<String> groceries = new ArrayList<String>();
groceries.add("Croissant");
groceries.add("Espresso");
IO.println(groceries); // [Croissant, Espresso]
}
Notice that the variable type is List<String>, while the object we create is new ArrayList<>(). This is common Java style: code against the interface when the interface gives you everything you need. If you later decide to use a different List implementation, the rest of the code can often stay the same.
Read: Retrieve an element by index:
IO.println(groceries.get(0)); // Croissant
Update: Replace an element at an index:
groceries.set(0, "Choco-Croissant");
IO.println(groceries); // [Choco-Croissant, Espresso]
Delete: Remove an element by index:
groceries.remove(1);
IO.println(groceries); // [Croissant]
Delete: Remove an element by value
groceries.remove("Espresso")
IO.println(groceries); // [Croissant]
<aside> ⌨️
Hands on: Modify the grocery list code above to add three more items. Then print the size of the list using groceries.size().
</aside>
ArrayList:The ArrayList signature in ArrayList.java is:
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
ArrayList<E> means the class has a type parameter named E. The letter “E” usually stands for element. In List<String> groceries, the element type is String. You’ll learn more about this in the Generics chapter.extends AbstractList<E> means ArrayList inherits shared list behavior from an abstract class. AbstractList is not complete enough to instantiate directly.implements List<E> means ArrayList promises to provide the behavior required by the List interface.Since Java already knows the element type from List<String> groceries, you do not need to repeat it on the right side. This is why new ArrayList<>() is valid.
A Set is a collection that contains no duplicate elements.
Use a Set when uniqueness matters more than position. For example, a user should not receive the same notification twice, and a product should not have the same tag repeated several times.
HashSet is the main implementation of Set. It uses a hash table, an internal structure that uses each value’s hash code to support fast lookup.
HashSet does not guarantee iteration order, so there is no index for retrieving elements.
<aside> 💡
</aside>
Initializing a set and adding elements:
void main() {
Set<Integer> userIds = new HashSet<>();
userIds.add(40012);
userIds.add(30001);
userIds.add(40011);
IO.println(userIds); // [30001, 40011, 40012]
}
<aside> ⚠️
</aside>
Read: A set has no get() method because it has no index. Instead, you usually ask whether the set contains a value. Also called membership checks:
void main() {
Set<Integer> approvedUserIds = new HashSet<>();
approvedUserIds.add(40012);
approvedUserIds.add(30001);
approvedUserIds.add(40011);
String response = approvedUserIds.contains(40011)
? "User is approved"
: "User is not approved";
IO.println(response);
}
Update/Delete: A set does not provide a set() method like a list. To change a value in a set, remove the old value and add the new one:
userIds.remove(40011);
userIds.add(20011);
IO.println(userIds); // [30001, 20011, 40012]
HashSet:The HashSet signature looks similar to ArrayList: