Course outline · 0% complete

0/29 lessons0%

Course overview →

ArrayList

lesson 7-1 · ~6 min · 19/29

From lesson 3-3, the array int[] scores = {90, 72, 88} can never grow to hold a fourth element.

Arrays are fixed-size for life. Three slots at creation means three slots forever, though the elements themselves are mutable and the array loops and passes around fine.

When you need a list that grows the way Python's does, you need ArrayList, which is this lesson's topic.

ArrayList, the growable list

Arrays from lesson 3-3 have a hard limitation: the size is fixed at creation, and real data rarely announces its size in advance. Rows returned by a database, lines in a file, and tasks a user keeps adding are all unknown until they arrive.

Working around it by hand means allocating a bigger array and copying everything over each time you outgrow the old one. ArrayList is that workaround done for you, and it is the single most used collection in Java code.

It is Java's version of the Python list, and it lives in java.util, so the file starts with an import:

import java.util.ArrayList;

ArrayList<String> tasks = new ArrayList<>();
tasks.add("write code");   // append
tasks.get(0)               // read by index
tasks.set(0, "plan")       // replace by index
tasks.remove(1)            // delete by index
tasks.size()               // length
tasks.contains("plan")     // membership test

The <String> is a type parameter, declaring that this list holds Strings and nothing else, checked at compile time. Adding an int to it will not compile, so a list can never end up with a surprise element type in it.

The empty <> on the right side is allowed because the compiler already knows the type from the left. Printing an ArrayList shows [a, b, c], which is far friendlier than what printing an array gives you.

A tour of the list methods

Add three items, inspect the list four ways, then remove one.

import java.util.ArrayList;

public class Main {
  public static void main(String[] args) {
    ArrayList<String> tasks = new ArrayList<>();
    tasks.add("write code");
    tasks.add("test code");
    tasks.add("ship it");
    System.out.println(tasks);
    System.out.println(tasks.size());
    System.out.println(tasks.get(0));
    System.out.println(tasks.contains("ship it"));
    tasks.remove(1);
    System.out.println(tasks);
  }
}

Output

[write code, test code, ship it]
3
write code
true
[write code, ship it]

The import line sits above the class, not inside it, and without it the class name ArrayList means nothing to the compiler.

remove(1) deleted test code and everything after it shifted down, so ship it now lives at index 1. Indexes in a list are positions rather than permanent labels, which matters when removing inside a loop.

Lists of numbers

Type parameters must be object types, so a primitive int is not allowed. Each primitive has a wrapper class to use instead:

PrimitiveWrapper
intInteger
doubleDouble
booleanBoolean
charCharacter
ArrayList<Integer> scores = new ArrayList<>();
scores.add(90);             // int auto-wraps into Integer
int first = scores.get(0);  // and unwraps back

That automatic conversion is called autoboxing, and you mostly never notice it happening.

It does leave one trap worth memorizing. scores.remove(1) removes index 1, while scores.remove(Integer.valueOf(90)) removes the value 90, because one overload takes an int position and the other takes an object.

The enhanced for from lesson 3-3 works on any collection, so for (String t : tasks) reads exactly as it did for arrays.

Choosing between an array and an ArrayList

Both hold an ordered sequence, and the deciding question is whether the size changes:

ArrayArrayList
Sizefixed at creationgrows and shrinks
Lengtha.lengthlist.size()
Reada[0]list.get(0)
Element typesprimitives or objectsobjects only, so Integer rather than int

Default to ArrayList, which is what nearly all application code does, because data changes.

Arrays remain the right call when the size is truly fixed and known, such as new int[26] for letter counts, and in performance-critical numeric code, since primitives in an array avoid the wrapper objects from the previous section.

Interview problems hand you arrays constantly, so both stay in your working vocabulary regardless of what your own code prefers.

Building, mutating, then summing a list

Four adds, one removal by index, and the accumulator pattern from lesson 3-3.

import java.util.ArrayList;

public class Main {
  public static void main(String[] args) {
    ArrayList<Integer> scores = new ArrayList<>();
    scores.add(90);
    scores.add(72);
    scores.add(88);
    scores.add(100);
    scores.remove(1);
    System.out.println(scores);
    int sum = 0;
    for (int s : scores) {
      sum += s;
    }
    System.out.println("sum: " + sum);
  }
}

Output

[90, 88, 100]
sum: 278

remove(1) deletes the element at index 1, which is 72 after the four adds, leaving 90 plus 88 plus 100 to total 278.

The loop declares int s while the list holds Integer, and autoboxing unwraps each element on the way in. The loop body is identical to the array version, which is the payoff of the enhanced for working on every collection.