Skip to content
wannacode

A list that grows

Problem

The cart is full

You are writing the shopping cart of an online store, and you keep the items in an array. But an array has a size that does not change. When the customer adds a fourth item, you make a bigger array and copy everything. When they remove an item, a hole is left:

String[] cart = { "Bread", "Milk", "Coffee" };
// A fourth item: the array is full, so we make a bigger one and copy.
String[] bigger = new String[cart.length + 1];
for (int i = 0; i < cart.length; i++) {
bigger[i] = cart[i];
}
bigger[cart.length] = "Butter";
cart = bigger;
// The customer removes the milk.
cart[1] = null;
for (String item : cart) {
System.out.println(item);
}
Output
Bread
null
Coffee
Butter
Now every loop over the cart has to skip null. And the number of items is no longer cart.length.

That is a lot of code for something every store does all the time.

The idea

ArrayList: an array that looks after itself

Java has a ready-made class for this: ArrayList. It keeps the elements in an array, but it does the copying and shifting by itself. A collection like this is called a list:

Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> cart = new ArrayList<>();
cart.add("Bread");
cart.add("Milk");
cart.add("Coffee");
cart.add("Butter");
cart.remove("Milk");
System.out.println(cart);
System.out.println("Items: " + cart.size());
}
}
Output
[Bread, Coffee, Butter]
Items: 3
  • ArrayList is in the java.util package, so the file starts with an import, as with Scanner.
  • add adds at the end, and remove removes and closes the hole: "Coffee" moves into the place of "Milk".
  • size() is how many elements there are right now. It is not length, because a list is not an array.
  • println prints the whole list in square brackets.

Here is what ArrayList does inside. The number in the parentheses is the starting size of the array. Without it, it is 10. Here it is 3, so you can see what happens when the array fills up:

ArrayList<String> cart = new ArrayList<>(3);
cart.add("Bread");
cart.add("Milk");
cart.add("Coffee");
cart.add("Butter");
cart.remove("Milk");
System.out.println(cart);
System.out.println("Items: " + cart.size());
Step by step, in words
  1. cart points to an ArrayList object. Inside there is an array with 3 places and a size counter.
  2. add puts each element in the first free place and increases size.
  3. The array is full. ArrayList makes a new one with 4 places and copies the three elements into it.
  4. The object now points to the new array, and nobody needs the old one. "Butter" goes into the free place.
  5. remove removes "Milk", and everything after it moves one place to the left. No hole is left.
  6. From the outside you see only a list of three items: ArrayList does the copying and the shifting.

The idea

What goes in the angle brackets

ArrayList<String> means "a list of text". The type in the angle brackets is called a type parameter (generics). It tells the compiler what is in the list, and the compiler enforces it:

Main.java
import java.util.ArrayList;
public class Main {
public static void main(String[] args) {
ArrayList<String> cart = new ArrayList<>();
cart.add("Bread");
cart.add(5);
}
}
Compile error
Main.java:7: error: incompatible types: int cannot be converted to String
cart.add(5);
^

That is why cart.get(0) returns a String directly, with no conversion.

On the right, the angle brackets are empty: new ArrayList<>(). The compiler takes the type from the left side.

Usually the variable is declared with the List interface, and the ArrayList class appears only after new:

List<String> cart = new ArrayList<>();
cart.add("Bread");
System.out.println(cart.get(0));

This is the polymorphism from the "Inheritance" module: List says what a list can do, and ArrayList is one way of doing it. You need two imports: java.util.ArrayList and java.util.List. From here on, the lesson does not show them.

The idea

Indexes: get, set, add, indexOf

A list has indexes like an array, from 0 to size() - 1. But instead of [ ] you use methods:

List<String> tasks = new ArrayList<>();
tasks.add("March invoice");
tasks.add("Call the bank");
tasks.add("Warehouse report");
System.out.println(tasks.get(0));
tasks.set(1, "Meeting at the bank");
tasks.add(0, "Coffee with the team");
System.out.println(tasks);
System.out.println(tasks.indexOf("Warehouse report"));
System.out.println(tasks.contains("Call the bank"));
Output
March invoice
[Coffee with the team, March invoice, Meeting at the bank, Warehouse report]
3
false
  • get(i) reads, and set(i, x) replaces.
  • add(i, x) inserts at index i and shifts the rest to the right.
  • indexOf(x) searches and returns the index or -1, and contains(x) only says whether it is there.
More precisely, if you are curious
  • An index outside the list stops the program, as with an array:
tasks.get(3) with 3 tasks
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 3 out of bounds for length 3
  • contains and indexOf compare with equals, not with ==. That is why they find text that is equal, not only the same object.
  • Inserting and removing at the front are slow in a big list: every element after the index moves by one place. For a million elements, that is a million moves.

Try it

Who is next?

The list of patients waiting at the doctor's. Ivan goes in, and Ana has an appointment, so she takes second place.

What will the program print?

List<String> waiting = new ArrayList<>();
waiting.add("Ivan");
waiting.add("Maria");
waiting.add("Peter");
waiting.remove(0);
waiting.add(1, "Ana");
System.out.println(waiting + " " + waiting.size());
Answer

The idea

Looping over the elements

You loop over a list like an array: with for-each when you need only the elements, or with an index when you also need the number:

List<String> cart = new ArrayList<>();
cart.add("Bread");
cart.add("Coffee");
cart.add("Butter");
for (String item : cart) {
System.out.println("- " + item);
}
for (int i = 0; i < cart.size(); i++) {
System.out.println((i + 1) + ". " + cart.get(i));
}
Output
- Bread
- Coffee
- Butter
1. Bread
2. Coffee
3. Butter
More precisely, if you are curious
  • Do not remove elements from a list while you loop over it with for-each. The program stops:
for (String item : cart) {
if (item.equals("Bread")) {
cart.remove(item);
}
}
Error
Exception in thread "main" java.util.ConcurrentModificationException

The exception does not always come. If you remove the second to last element, the loop stops early and the last one is never checked. So do not rely on it.

  • If you have to remove, loop with an index from the back to the front. That way, removing does not shift the elements you have not checked yet:
List<String> cart = new ArrayList<>();
cart.add("Bread");
cart.add("Milk");
cart.add("Bread");
for (int i = cart.size() - 1; i >= 0; i--) {
if (cart.get(i).equals("Bread")) {
cart.remove(i);
}
}
System.out.println(cart);
Output
[Milk]
In the "Modern Java" module you will see a shorter way: removeIf.

The idea

Numbers in a list: Integer

A list of scores looks like this, but it does not compile:

ArrayList<int> scores = new ArrayList<>();
Compile error
Main.java:5: error: unexpected type
ArrayList<int> scores = new ArrayList<>();
^
required: reference
found: int

The angle brackets need a class, and int is not a class. So every primitive type has a wrapper class: Integer for int, Double for double, Boolean for boolean, Character for char.

List<Integer> scores = new ArrayList<>();
scores.add(85);
scores.add(64);
scores.add(91);
int total = 0;
for (int score : scores) {
total += score;
}
System.out.println("Average: " + (double) total / scores.size());
Output
Average: 80.0

scores.add(85) gets an int, but the list wants an Integer. Java puts it in a wrapper by itself. This is called autoboxing. In the loop, int score is the opposite: unboxing. Almost always, you do not notice the difference.

More precisely, if you are curious
  • Compare Integer values with equals, not with ==. == checks whether the two variables point to the same object:
Integer a = 127, b = 127;
Integer c = 128, d = 128;
System.out.println(a == b);
System.out.println(c == d);
System.out.println(c.equals(d));
Output
true
false
true

Java keeps ready-made objects for the numbers from -128 to 127, so at 127 both arrows point to one object. At 128 there are two objects.

  • An Integer can be null, and an int cannot. If you unbox null, the program stops with NullPointerException. You will run into this in the lesson on HashMap.

Try it

Find the bug

A theater keeps the taken seats in a list. A customer gives up seat 2, and the program should print [12, 7, 5]. Instead it prints [12, 2, 5]. Which line has the bug?

Pick the line with the bug.

The idea

Sorting and a ready-made list

Collections.sort sorts the list in place: text in alphabetical order, numbers by size. The Collections class is in java.util too.

List<String> cities = new ArrayList<>();
cities.add("Sofia");
cities.add("Plovdiv");
cities.add("Varna");
Collections.sort(cities);
System.out.println(cities);
List<Integer> prices = new ArrayList<>(List.of(30, 5, 12));
Collections.sort(prices);
System.out.println(prices);
Output
[Plovdiv, Sofia, Varna]
[5, 12, 30]

List.of(30, 5, 12) makes a ready-made list from the values you list. But it is immutable: you cannot add, remove or sort in it.

List<String> days = List.of("Mon", "Tue", "Wed");
days.add("Thu");
Error
Exception in thread "main" java.lang.UnsupportedOperationException

That is why the prices above are copied into a new ArrayList: new ArrayList<>(List.of(30, 5, 12)) is a list you can change.

More precisely, if you are curious
  • Text is sorted by the codes of its characters. Capital letters come before lowercase ones, so "Pear" comes before "apple":
List<String> fruit = new ArrayList<>(List.of("apple", "Pear", "banana", "Grape"));
Collections.sort(fruit);
System.out.println(fruit);
Output
[Grape, Pear, apple, banana]
  • How to sort your own objects, for example products by price, you will see in the lesson "Classes inside classes".

Exercise

Lists in classes

Solve the tasks in your editor. The list is a private field of the class, and main only calls its methods.

1. A bookshelf. A BookShelf class with a addBook(String title) method that puts the book at the end, and takeBook(), which returns and removes the first book (or null if the shelf is empty). Add count() and toString(). In main, add "Under the Yoke", "Tobacco" and "The Iron Candlestick", take one book and print the rest.

Output
Took: Under the Yoke
2 left: [Tobacco, The Iron Candlestick]

2. An event log. An Event class with a date and a description, and an EventLog class with the methods addEvent(String date, String description) and getEventsBetween(String from, String to), which returns a list of the events between the two dates, inclusive. The dates are text in the form 2026-03-31. In this form, the text sorts the same way as the dates, so compareTo from the lesson on text compares them correctly. Add four events: 2026-01-15 "Annual stocktake", 2026-03-31 "First quarter report", 2026-05-20 "New coffee supplier", 2026-09-01 "New school year". Print the ones from 2026-03-01 to 2026-06-30.

Output
2026-03-31 First quarter report
2026-05-20 New coffee supplier

3. A notebook. A Note class with a title and content, and a Notebook class with the methods addNote, findNote(String title) (returns the note or null), deleteNote(String title) (returns true if it deleted something) and count(). Add the notes "OOP", "Collections" and "SQL", find "Collections", delete "OOP" and check that it is gone.

Output
Collections: ArrayList grows by itself
Deleted: OOP
Notes: 2
There is no OOP note.

4. Mental math. A Question class with a text and the right answer, and a MathGame class with a list of questions, an addQuestion method and a start(Scanner in) method. start asks the questions one by one, reads the answer and at the end says how many were right. The questions are 2 + 2, 3 * 4 and 15 - 9.

Input
4
12
7
Output
2 + 2 = Correct!
3 * 4 = Correct!
15 - 9 = Wrong, the answer is 6.
Score: 2 out of 3

More tasks

  • No repeats. Read 6 names and collect them in a list, skipping the ones that are already in it. For the input Ivan, Maria, Ivan, Ana, Maria, Peter the output is [Ivan, Maria, Ana, Peter].
  • The most expensive item. A List<Double> holds prices. Find the highest one and its index, without Collections.
  • Reverse the list. Write a method that returns a new list with the same elements in reverse order. Do not change the original.
  • A shopping list. Read the commands add milk, remove milk, list and end, and keep the list between them. The command to remove an item that is not there says "No such item."

Test

Check yourself

1. Why is an ArrayList handier than an array for a shopping cart?
2. What does <String> say in List<String> cart?
3. List<Integer> seats holds [12, 2, 7, 5]. What is left after seats.remove(2)?
4. Why can you not write ArrayList<int>?
5. List<String> days = List.of("Mon", "Tue"); What happens at days.add("Wed")?

Made by Ivo Gergov as part of Evo Tech's campaign for accessible programming education ·