Skip to content
Ameen Abozaid
PublishedBEG-030-L01

Beyond arrays: the Collections Framework

  • Lesson 1 of 9
  • 11 min read
  • Beginner
On This Lesson 11 topics

Lesson Overview

In Module BEG-015 you stored several values in an array. Arrays are useful, but they have a fixed size, and every real program quickly wants things an array does not give you: add a value at the end, remove one from the middle, check "have I seen this before?", or look something up by a name instead of a position.

Java answers with the Collections Framework: a family of ready-made containers in the java.util package that grow and shrink on their own. This lesson gives you the map of that family. There are four main kinds, and the rest of the module is about using each one well:

  • List: an ordered sequence, duplicates allowed (a numbered shelf).
  • Set: a bag of unique values (a bag of unique tokens).
  • Map: key-value pairs (a phone book).
  • Queue: a waiting line (a ticket queue).

We also meet two habits you will use in every lesson: declaring a variable by its interface type, and the <String> notation. You do not need to understand that notation yet; it is explained in BEG-031, and here we only read it as "a list of strings".

This lesson builds on arrays (BEG-015), interfaces (BEG-024), and equals/hashCode (BEG-026).

Learning Objectives

By the end of this lesson you will be able to:

  • Explain three limits of arrays that collections remove.
  • Name the four main collection kinds and say what each is for.
  • Say that all of them live in java.util and need an import.
  • Read List<String> as "a list of strings" and explain why List<int> is not allowed.
  • Declare a variable with an interface type and create it with a class: List<String> x = new ArrayList<>();.
  • Explain, in one sentence, what an interface-typed variable lets you change later.

Written Explanation

The three limits of arrays

An array has a size fixed when you create it.

java
String[] arr = new String[3];
arr[0] = "Ana"; arr[1] = "Omar"; arr[2] = "Lina";
arr[3] = "Sam";   // fails at run time

The fourth assignment throws an ArrayIndexOutOfBoundsException with the message Index 3 out of bounds for length 3. The array cannot grow. If you want room for a fourth name you must create a bigger array and copy everything over yourself.

Three limits follow from this design:

  1. Fixed size. You must know the number of elements in advance, or waste space, or copy.
  2. Awkward adding and removing. Removing the middle name means shifting every later name one place to the left by hand.
  3. Only position access. "Is Omar in here?" means writing a loop. There is no notion of "unique" or "look up by key".

Collections grow and shrink for you

The same task with a collection:

java
List<String> names = new ArrayList<>();
names.add("Ana"); names.add("Omar"); names.add("Lina");
names.add("Sam");              // no problem: the list grows
names.remove("Omar");          // later items shift for us

Printing it gives [Ana, Lina, Sam]. The collection manages its own storage. The Java documentation describes ArrayList as a "resizable-array implementation" of List: inside there is still an array, but the class replaces it with a larger one when needed, so you do not have to.

The four main kinds

KindPictureKey promiseTypical question it answers
ListNumbered shelfKeeps the order; duplicates allowed; access by position 0, 1, 2..."What is the third item?"
SetBag of unique tokensNo duplicate elements"Have I seen this already?"
MapPhone bookEach key maps to at most one value"What is the value for this key?"
QueueTicket queueItems leave in a defined order, usually first in, first out"Who is next?"

List, Set, and Queue are all kinds of Collection (a group of elements). A Map is the odd one out: it is not a Collection, because it stores pairs rather than single elements. Each of these four is an interface: a contract (BEG-024). The classes that do the work are implementations: ArrayList and LinkedList for lists, HashSet and TreeSet for sets, HashMap and TreeMap for maps, ArrayDeque for queues. Lessons 2 to 5 take them one family at a time, and Lesson 6 gives a decision chart.

Where they live: java.util

All of these are in the java.util package, so you import them (BEG-027):

java
import java.util.List;
import java.util.ArrayList;
// or all at once
import java.util.*;

Without the import, the compiler cannot find the name and reports an error.

Reading List<String>

The part in angle brackets says what the collection holds. List<String> is "a list of strings", and Map<String, Integer> is "a map from strings to integers". The compiler uses it to stop you putting the wrong kind of thing in. How this works is the subject of BEG-031 (generics); for now, just read and write it.

One consequence you can use today: the type in the brackets must be a reference type. List<int> does not compile; the compiler says unexpected type, required: reference, found: int. For numbers, use the wrapper class: List<Integer>. Java converts between int and Integer automatically in most places (called autoboxing), so list.add(5) works on a List<Integer>. We will rely on this and flag the few places where it surprises people.

Declare by interface, create by class

Look closely at this line:

java
List<String> names = new ArrayList<>();

The variable type is the interface List. The object is an ArrayList. The <> after ArrayList is the diamond: you leave it empty and the compiler fills in String from the left-hand side.

Why not ArrayList<String> names = new ArrayList<>();? Both compile. The interface version says "I only need list behavior", which is true of almost all code. The benefit is flexibility: if you later decide a LinkedList fits better, you change one word on the right-hand side and nothing else in the code breaks, because everything else only used List methods. This is "programming to an interface" from BEG-024-L05, applied to collections. A method that only needs to read items can go even more general and accept a Collection.

Examples

Example 1: array versus list (simple)

java
import java.util.*;

public class L01a {
    public static void main(String[] args) {
        String[] arr = new String[3];
        arr[0] = "Ana"; arr[1] = "Omar"; arr[2] = "Lina";

        List<String> names = new ArrayList<>();
        names.add("Ana"); names.add("Omar"); names.add("Lina");
        names.add("Sam");
        System.out.println(names + " size=" + names.size());
        names.remove("Omar");
        System.out.println(names);

        try { arr[3] = "Sam"; }
        catch (ArrayIndexOutOfBoundsException e) { System.out.println(e.getMessage()); }
    }
}

Output:

text
[Ana, Omar, Lina, Sam] size=4
[Ana, Lina, Sam]
Index 3 out of bounds for length 3

The list accepted a fourth item and closed the gap after remove; the array could do neither.

Example 2: one of each kind (practical)

java
List<String> queueOrder = new ArrayList<>(List.of("b", "a", "b"));
Set<String> unique = new HashSet<>(queueOrder);
Map<String, Integer> ages = new HashMap<>();
ages.put("Ana", 30);
Queue<String> line = new ArrayDeque<>();
line.offer("Ana"); line.offer("Omar");
System.out.println(queueOrder + " " + unique.size() + " " + ages + " " + line);

Output: [b, a, b] 2 {Ana=30} [Ana, Omar].

The list keeps both bs, the set keeps one, the map shows its key-value pair in braces, and the queue shows its contents front first. Note List.of(...) is a quick way to make a small list; the result cannot be changed (Lesson 2 explains), so we copy it into a new ArrayList.

Example 3: one method for many collections (realistic)

A small reporting method only needs to read items. Taking a Collection<String> lets it accept a list, a set, or a queue.

java
static void printAll(Collection<String> items) {
    for (String s : items) System.out.println("- " + s);
}

List<String> list = new ArrayList<>(List.of("x", "y"));
Set<String> set = new TreeSet<>(List.of("q", "p"));
printAll(list);
printAll(set);

Output:

text
- x
- y
- p
- q

The list prints in insertion order. The TreeSet printed p before q although we gave q first: a TreeSet keeps its elements sorted (Lesson 3). Same method, different containers, and the method never needed to know which.

Backend Insight

Backend code is mostly collections of things. A request handler receives a list of order lines, groups them in a map by product, de-duplicates ids with a set, and feeds jobs to a queue. Later modules (Spring, JPA) return List<Customer> from repository methods and Map<String, String> for HTTP headers, so the four interfaces from this lesson will be in the signature of most methods you read.

Declaring by interface matters more in backend code than anywhere: a repository method that returns List<Order> promises a list without promising which class. The team can change the implementation without touching every caller. Also, the choice of collection affects speed on large data; Lesson 9 measures that. The internals of HashMap and the rest are covered in BEG-040 and BEG-041, so here we focus on using them.

Common Mistakes

Mistake 1: Using an array out of habit

An array is the right tool when the size is truly fixed and known. If you find yourself copying arrays to "grow" them, use an ArrayList.

Mistake 2: Forgetting the import

  • Incorrect: using List or ArrayList with no import java.util....
  • Correct: add import java.util.List; and import java.util.ArrayList; (or import java.util.*;).

The compiler error is "cannot find symbol", the same one you met in BEG-008 for any unknown name.

Mistake 3: Writing a primitive in the brackets

  • Incorrect: List<int> ages = new ArrayList<>(); (compile error: unexpected type).
  • Correct: List<Integer> ages = new ArrayList<>();.

Mistake 4: Declaring the variable as the concrete class everywhere

ArrayList<String> names = new ArrayList<>(); works, but now your fields, parameters, and return types all say ArrayList, and switching implementation means editing all of them. Prefer List<String> unless you truly need a method that only ArrayList offers.

Mistake 5: Thinking a Map is a Collection

A Map stores pairs, so it does not implement Collection. You cannot pass a Map to printAll(Collection<String>). To work with its contents as collections, ask the map for its keySet(), values(), or entrySet() (Lesson 4).

Exercises

Try each exercise yourself before opening the solutions.

Level A: Foundation

A1. Name the four main collection kinds and give a one-line purpose for each.

Show solution A1

A1. List: ordered, duplicates allowed, access by position. Set: unique elements only. Map: key-value pairs, one value per key. Queue: a waiting line, usually first in, first out.

A2. Which package contains the Collections Framework, and what must you write to use ArrayList without typing its package every time?

Show solution A2

A2. java.util. Add import java.util.ArrayList; (or import java.util.*;) at the top of the file.

A3. Which line does not compile, and why?

java
List<String> a = new ArrayList<>();
List<double> b = new ArrayList<>();
Show solution A3

A3. The second line. A type argument must be a reference type, so List<double> fails with unexpected type. Use List<Double>.

Level B: Application

B1. Write code that creates a List<String> of three cities, adds a fourth, removes the first one by value, and prints the list and its size.

Show solution B1

B1.

java
List<String> cities = new ArrayList<>();
cities.add("Cairo"); cities.add("Riyadh"); cities.add("Rabat");
cities.add("Tunis");
cities.remove("Cairo");
System.out.println(cities + " size=" + cities.size());

This prints [Riyadh, Rabat, Tunis] size=3.

B2. For each need, say which of List, Set, Map, Queue fits best: (a) the order in which print jobs arrived, (b) the unique email addresses in a mailing list, (c) a product's price by its code, (d) the five most recent log lines, in order, with repeats allowed.

Show solution B2

B2. (a) Queue. (b) Set. (c) Map. (d) List, because order matters and repeats are allowed (a queue would also work if you only process from the front).

B3. Rewrite ArrayList<Integer> scores = new ArrayList<Integer>(); using the interface type and the diamond, and say what each change gives you.

Show solution B3

B3. List<Integer> scores = new ArrayList<>();. The interface type on the left lets you change the implementation later by editing only the right-hand side. The diamond stops you repeating Integer and lets the compiler infer it.

Level C: Challenge

C1. Write static int countLong(Collection<String> words) that returns how many words have more than four characters. Call it with a List, a Set, and a Queue to show that one method serves all three.

Show solution C1

C1.

java
static int countLong(Collection<String> words) {
    int n = 0;
    for (String w : words) {
        if (w.length() > 4) n++;
    }
    return n;
}

Call it with new ArrayList<>(List.of("apple", "fig")), new HashSet<>(List.of("banana", "kiwi")), and new ArrayDeque<>(List.of("melon")). All three compile because each is a Collection<String>. Counting n is not affected by the collection kind.

C2. A friend stores customer ids in an array of size 100 and says "it will do". Give two concrete problems that could appear in a growing system, and name the collection you would move to.

Show solution C2

C2. Problems: the 101st customer has nowhere to go (an ArrayIndexOutOfBoundsException or silent data loss), and removing a customer leaves a gap or forces manual shifting; also checking "does id X exist?" needs a loop. Move to a List<Integer> (or a Set<Integer> if only existence checks matter, or a Map if you look customers up by id).

Mini Challenge

Write a tiny program with a main that does the following:

  1. Creates a List<String> containing five words, one of which repeats.
  2. Copies it into a Set<String> and prints the size of both.
  3. Creates a Queue<String> and offers the first two words to it, then prints the queue.
  4. Creates a Map<String, Integer> that maps each distinct word to its length, and prints it.

Check your work: the list size is greater than the set size by exactly the number of repeats, every variable is declared with an interface type, and the program compiles with import java.util.*;.

Summary & Next Lesson

Key takeaways

  • Arrays have a fixed size and only offer positional access; collections grow, shrink, and offer richer operations.
  • The four main kinds are List, Set, Map, and Queue. Map is not a Collection.
  • They live in java.util.
  • List<String> means a list of strings; type arguments must be reference types, so use Integer, not int.
  • Declare by interface and create by class: List<String> x = new ArrayList<>();.

Next lesson

The next lesson, "Lists", goes deep on the most common collection: ArrayList, its core methods, and how it differs from LinkedList.

Self-check

  1. Name two things an array cannot do that an ArrayList can.
  2. Which collection kind does not allow duplicate elements?
  3. Why does List<int> not compile, and what do you write instead?
  4. In List<String> x = new ArrayList<>();, which part is the interface and which is the class?
  5. Is a Map a Collection?
Show answers
  1. It cannot grow after creation, and it has no built-in add or remove that shifts items for you.
  2. Set.
  3. Type arguments must be reference types, not primitives. Write List<Integer>.
  4. List is the interface (the variable type); ArrayList is the class that creates the object.
  5. No. A Map stores key-value pairs and is a separate branch; its keySet(), values(), and entrySet() views are collections.

References

  • Oracle, Java SE 21 API, ArrayList. Supports the "resizable-array implementation" description, and that ArrayList permits null and is not synchronized.
  • Oracle, Java SE 21 API, List. Supports that lists are ordered and typically allow duplicates, and that List.of creates unmodifiable lists.
  • Oracle, Java SE 21 API, Map. Supports that a map cannot contain duplicate keys and each key maps to at most one value.
  • Compiler messages and program output in this lesson were produced by compiling and running the examples with JDK 21. The statement that Map is not a Collection is standard Java API structure; I did not fetch a page that states it in those words.

Practice

Apply this lesson with the module's lab, exercises and quiz.

Lab and exercises Quiz

Marking a lesson complete is optional and you can undo it any time. Your progress stays in this browser.

Continue to next lesson

Type to search levels, phases, modules and lessons.