Skip to content
Theory · Collections and generics · lesson 1 of 13

Generics: List<String>

What you'll learn

What you'll learn:

  • declare a List<String> and explain what the type in angle brackets gives you;
  • add, change and remove ArrayList elements, and walk a list without breaking it;
  • explain how HashSet and HashMap find an element through hashCode and equals;
  • sort a list of your own objects with Comparable and Collections.sort.

Picture a theater cloakroom. The attendant does not know in advance how many people will come: ten or three hundred. So hats go on a long shelf with room for everyone, and coats hang on numbered hooks.

An array from the arrays theme does not suit such a cloakroom. It is a row of lockers whose number is set once, when it is created. More guests arrive, and you need a new array and have to move every item over by hand.

For jobs like this, Java has collections. A collection is an object that holds a group of other objects and grows by itself as more arrive. This whole theme is about collections, and we start with a list:

JAVA
import java.util.ArrayList;
import java.util.List;

public class Main {
    public static void main(String[] args) {
        List<String> guests = new ArrayList<>();
        guests.add("Anna");
        guests.add("Boris");
        System.out.println(guests);        // [Anna, Boris]
        System.out.println(guests.size()); // 2
        String first = guests.get(0);
        System.out.println(first);         // Anna
    }
}

Let's go through the program line by line:

  1. The two import lines bring in the names ArrayList and List from the package java.util, where the collections live.
  2. List<String> guests = new ArrayList<>(); creates an empty list of strings.
  3. guests.add("Anna") puts a string at the end of the list. The list grows by itself: nobody set its size in advance.
  4. System.out.println(guests) prints the elements in square brackets: [Anna, Boris].
  5. guests.size() returns the number of elements, 2. For an array it is the field length, and for a list it is the method size().
  6. guests.get(0) returns the element at index 0. Indices start at zero, as in an array.

The type in angle brackets

List<String> reads as "a list of strings". The angle brackets hold the type of the elements, and it is part of the variable's own type. Classes and interfaces that take a type as a parameter are called generics. The type in the angle brackets is a type parameter.

Back to the cloakroom. A type parameter is the sign above the shelf: "Hats only". The compiler watches the sign more strictly than any attendant and will not let you put an umbrella on the shelf:

JAVA
List<String> guests = new ArrayList<>();
guests.add(42); // ✗
TEXT
error: incompatible types: int cannot be converted to String

The compiler catches the error before the program runs. In return, everything in a List<String> is sure to be a string. So guests.get(0) returns a String right away, and you do not cast it by hand.

On the right, in new ArrayList<>(), the angle brackets are empty: the compiler takes the type from the left side by itself. new ArrayList<String>() is correct too, only longer.

List and ArrayList

The left side says List and the right side says ArrayList, and that is not a typo. List is an interface, like Courier from the interfaces theme. It lists what any list can do: add, get, size and other methods. ArrayList is a class that implements this interface and keeps the elements in an array.

You declare the variable with the interface type and name the concrete class only after new. If you later need a different list, one word on the right changes, and the code that works with guests stays the same. Why List on the left and not ArrayList is a common interview question.

Numbers in a list: List<Integer>

A type parameter can only be a reference type, such as a class, an interface or a record. A primitive such as int does not fit there, so List<int> does not compile:

TEXT
error: unexpected type

Below it, javac explains: required: reference, found: int. A reference type is needed, and a primitive was found.

For numbers, you take the wrapper class from the wrappers theme: List<Integer>. Autoboxing does the rest. On add it puts the int coin into an Integer envelope, and when an int is needed, it takes the coin back out:

JAVA
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(7);
int sum = nums.get(0) + nums.get(1);
System.out.println(sum); // 12

nums.add(5) boxes 5 into an Integer. In the line with the sum, get returns two Integers, and they are unboxed for the addition. Other primitives work the same way: List<Double>, List<Boolean>, List<Character>.

Here the shelf picture is not exact. A list's shelf holds not the objects themselves but references to them, like a variable from the classes theme. That is why one and the same object can sit in two lists at once.

Common mistake

Only ArrayList was imported:

JAVA
import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        List<String> guests = new ArrayList<>(); // ✗
    }
}
TEXT
error: cannot find symbol

The program does not compile. The compiler knows the name ArrayList but not List, and below the message javac names it: symbol: class List. The line import java.util.ArrayList; brings in only the name ArrayList, so here you also need import java.util.List;.

Key points

  • Collections from the package java.util hold a group of objects and grow by themselves; import java.util.ArrayList; brings in only ArrayList, and List takes a line of its own.
  • List<String> is a list of strings: the angle brackets hold the type parameter, and the compiler lets no other type into the list.
  • Declare the variable with the interface List and create the object with new ArrayList<>(); the empty brackets <> take the type from the left.
  • A type parameter is a reference type only: List<int> does not compile, and numbers go into a List<Integer>.