Generics: List<String>
What you'll learn:
- declare a
List<String>and explain what the type in angle brackets gives you; - add, change and remove
ArrayListelements, and walk a list without breaking it; - explain how
HashSetandHashMapfind an element throughhashCodeandequals; - sort a list of your own objects with
ComparableandCollections.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:
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:
- The two
importlines bring in the namesArrayListandListfrom the packagejava.util, where the collections live. List<String> guests = new ArrayList<>();creates an empty list of strings.guests.add("Anna")puts a string at the end of the list. The list grows by itself: nobody set its size in advance.System.out.println(guests)prints the elements in square brackets:[Anna, Boris].guests.size()returns the number of elements,2. For an array it is the fieldlength, and for a list it is the methodsize().guests.get(0)returns the element at index0. 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:
List<String> guests = new ArrayList<>();
guests.add(42); // ✗error: incompatible types: int cannot be converted to StringThe 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:
error: unexpected typeBelow 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:
List<Integer> nums = new ArrayList<>();
nums.add(5);
nums.add(7);
int sum = nums.get(0) + nums.get(1);
System.out.println(sum); // 12nums.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:
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
List<String> guests = new ArrayList<>(); // ✗
}
}error: cannot find symbolThe 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.utilhold a group of objects and grow by themselves;import java.util.ArrayList;brings in onlyArrayList, andListtakes 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
Listand create the object withnew 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 aList<Integer>.