Java Collections

Data structures that hold groups of objects — starting with ArrayList for ordered lists and HashMap for fast lookups by key. These two types appear in virtually every non-trivial Java program.

The Problem Collections Solve: Arrays Don't Resize

If you've followed the tutorials in order, you've already hit the limitation. By the time you need to store an unknown number of items — processing lines from a file, collecting search results, building a list of employees — Java's basic arrays become awkward:

The problem with arrays
// You have to guess the maximum size upfront
String[] lines = new String[1000];  // What if the file has 1001 lines?

int count = 0;
for (String line : allLines) {
    if (count >= lines.length) {
        // Resize manually — error-prone and tedious
        lines = Arrays.copyOf(lines, lines.length * 2);
    }
    lines[count++] = line;
}

// Or: count first, then create the right-sized array
// (requires reading the file twice — wasteful for large files)

Collections solve this with a simple idea: a data structure that grows automatically. You just call add(item) and the internal storage resizes itself when needed. You never think about array sizes again.

The same task with ArrayList
List<String> lines = new ArrayList<>();

for (String line : allLines) {
    lines.add(line);  // That's it. No sizing, no resizing, no counting.
}

The Collections Framework is Java's unified architecture for these resizable data structures. It provides interfaces that define what a data structure does (a List is an ordered collection, a Map stores key-value pairs) and concrete classes that implement them (ArrayList, HashMap). This separation is central to how real Java code is written.

The Interfaces vs. Implementations Pattern

This is the most important concept in the Collections Framework, and the one that confuses newcomers the most. It's a two-level design:

Level Example Purpose
Interface List<String>
Map<String, Integer>
Defines the contract: "what can I do with this collection?" You write code against the interface, not the implementation.
Implementation ArrayList
HashMap
Provides the actual behavior: "how is the contract fulfilled?" You choose one when you create the object.

In practice, this means you write:

Write code against the interface
// The type is the INTERFACE, not the implementation
public void processNames(List<String> names) {
    for (String name : names) {
        System.out.println(name.toUpperCase());
    }
}

// The object is the IMPLEMENTATION
List<String> names = new ArrayList<>();

Why does this matter? Because you can change the implementation later without touching the method:

Swap the implementation, zero changes to the method
// Same method, same interface — works with LinkedList too
List<String> names = new LinkedList<>();

// If a method takes List, it accepts ArrayList, LinkedList, Vector,
// or any future List implementation — without changes.

The Rule of Thumb

Use the interface type for variable declarations, method parameters, and return types. Use the implementation class only on the line where you create the object with new. This is the standard convention in every professional Java codebase.

This pattern — writing code against interfaces — is also how Spring Boot works. When Spring injects a List<Employee> into your service, you don't care whether it's an ArrayList or a LinkedList. The framework decides. Your code stays decoupled from implementation details.

Before You Start

You should complete the Object-Oriented Programming section first. Specifically:

You should also be comfortable with basic generics syntax: List<String>, Map<String, Integer>. The tutorials introduce generics as they go, but if seeing angle brackets in type names is completely new to you, read the short "Generics at a Glance" section below first.

Generics at a Glance

Generics let you specify what type of objects a collection can hold. Without them, every collection would hold Object, and you'd need to cast every element:

Without generics (Java 1.4 and earlier)
// Without generics — every element is Object
List names = new ArrayList();
names.add("Alice");
names.add("Bob");

// No type safety — this compiles but crashes at runtime:
names.add(42);  // Integer in a List of Strings — no compile error!

// You have to cast every element when retrieving:
String first = (String) names.get(0);
With generics (Java 5+)
// With generics — the type is enforced at compile time
List<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");

// This is a COMPILE ERROR — caught before the program runs:
names.add(42);  // required type: String, found: Integer

// No casting needed:
String first = names.get(0);

The <String> in ArrayList<String> is a type parameter. It tells the compiler: "this list only accepts Strings." The same syntax applies to HashMap<String, Integer> — two type parameters because a map has a key type and a value type.

Raw Types Are Still Legal but Wrong

Writing List names = new ArrayList() (no type parameter) still compiles for backwards compatibility with pre-Java-5 code. Don't do it. It gives you no type safety and produces compiler warnings in any modern IDE. Always specify the type parameter.

Tutorials — Study in This Order

Tutorial 1 of 2 Beginner · ~15 min

ArrayList

How to create, add to, remove from, search within, and sort an ArrayList. Covers the most common operations you'll perform on lists in real applications, including iterating with enhanced for-each and finding elements by value. Also explains when to use ArrayList versus a plain array.

Why first: ArrayList is simpler than HashMap (one type parameter instead of two, no key-value concept to learn). It also introduces the List interface, which is used extensively in the Streams tutorial, the Spring Boot tutorial, and every framework you'll encounter.

Start tutorial →
Tutorial 2 of 2 Intermediate · ~15 min

HashMap

How to store and retrieve data by key with put() and get(), handle the case where a key doesn't exist with getOrDefault(), check for key existence, iterate over entries, and understand how hashing makes lookups fast. This is the data structure you'll use most often for building indexes, caches, and configuration maps.

Tutorial 2 of 2 Intermediate · ~15 min

HashMap

How to store and retrieve data by key with put() and get(), handle the case where a key doesn't exist with getOrDefault(), check for key existence, iterate over entries, and understand how hashing makes lookups fast. This is the data structure you'll use most often for building indexes, caches, and configuration maps.

Why second: HashMap introduces two new concepts at once — key-value pairs and hash-based lookup. It also uses Map.Entry (a nested interface — entry.getKey(), entry.getValue()), which builds on the interface knowledge from ArrayList. The JDBC tutorial's EmployeeDAO and the Spring Boot tutorial's entity classes all use HashMap-like patterns, so this concept pays off repeatedly.

Start tutorial →
Advertisement

After Completing These Tutorials

Store dynamic data

Collect an unknown number of results from files, databases, or user input without pre-sizing arrays.

Look up values by key

Build word counts, configuration maps, caches, and indexes where you need to find something by an identifier, not a position.

Use the List and Map interfaces

Write methods that accept List<T> and Map<K, V> so your code works with any implementation.

Read framework code

Understand what Spring Data JPA returns (List<Employee>), what Spring Boot injects (Map<String, String> for configuration), and what Stream operations produce.

What's Not Covered Here (and Why)

The Java Collections Framework has dozens of types. We focus on the two you'll use in almost every program. Here's what's deferred and when you'll encounter it:

A Set is a collection that cannot contain duplicates. It's useful for things like "track unique visitors" or "ensure no duplicate emails." You'll need it when you actually have that requirement — which will likely be in a project, not in a tutorial exercise. The API is nearly identical to List, so learning it takes about five minutes once you know ArrayList.

LinkedList implements both List and Deque (double-ended queue), so you can use it as a queue or a stack. This is a practical need in specific scenarios (BFS algorithms, task queues, undo/redo) — not a general concept that benefits from a standalone tutorial. When you need a queue, look at LinkedList's addFirst(), addLast(), pollFirst(), pollLast() methods.

TreeMap keeps keys sorted, so firstKey() and lastKey() are O(log n). LinkedHashMap maintains insertion order. These are specialized tools for specific situations (leaderboards, LRU caches, ordered configuration). You'll discover them when you need sorted maps or ordered iteration — not before.

Collections.sort(list), Collections.reverse(list), Collections.unmodifiableList(list) are static utility methods. They're useful but straightforward — you'll look them up when you need them. The ArrayList tutorial shows Collections.sort() in context, which is enough to get you started.

Sorting requires either implementing Comparable (on the class itself) or providing a Comparator (as a separate object). The ArrayList tutorial introduces Comparable briefly because it's the simplest path to sorting objects. A full Comparator tutorial belongs next to the Streams tutorial, where Comparator.comparing() is used heavily in pipeline operations.

Thread-safe collections are essential in multi-threaded applications (web servers handle many requests simultaneously). But learning them requires understanding threads first, which is a significant topic on its own. You'll encounter ConcurrentHashMap in the Spring tutorials because Spring uses it internally, but understanding why it's used requires thread knowledge.

The 80/20 Rule of Collections Knowledge

In practice, ArrayList and HashMap handle about 80% of the collection work you'll do in Java. Learning Set and Queue covers another 15%. The remaining 5% (TreeMap, LinkedHashMap, concurrent collections) are specialized tools you look up when a specific need arises. Don't try to learn all collection types upfront — it's more effective to learn them when you have a real problem to solve.

Where to Go After Collections

Streams

Streams operate on collections — filter, map, sort, group, and collect data without manual loops. You already know what an ArrayList and HashMap look like, so you can focus on what the stream operations do rather than what the data structure is.

Database (JDBC)

The JDBC tutorial's EmployeeDAO returns List<Employee> from queries. Spring Data JPA's findAll() returns a List. Every database operation in Java starts or ends with a collection. Understanding collections makes data access code readable.

Spring Boot

Spring controllers return List<ResponseDTO>, Spring Data JPA repositories return List<Entity>, and Spring configuration uses Map<String, Object>. Every piece of Spring is a collection of objects — collections knowledge is assumed throughout.