Java Arena
Step 4 of 7

Removing values

remove takes a value out of a list. It comes in two kinds:

  • list.remove(index) removes the value at that index.
  • list.remove(value) removes the first copy of that value. If the value isn't in the list, nothing happens.

When a value is removed, the list gets one shorter, and every value after it moves down one place: its index goes down by 1.

ArrayList<String> queue = new ArrayList<>();
queue.add("Ana");
queue.add("Ben");
queue.add("Cai");
queue.add("Dee");

queue.remove(0);
System.out.println(queue);
System.out.println("First now: " + queue.get(0));

queue.remove("Dee");
System.out.println(queue);
System.out.println("Size: " + queue.size());
Output
[Ben, Cai, Dee]
First now: Ben
[Ben, Cai]
Size: 2

The trap in number lists

In a list of numbers, numbers.remove(3) could mean "remove index 3" or "remove the value 3". Java always picks the index: an int in the parentheses is an index. To remove a value, give remove an Integer, with Integer.valueOf:

ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(8);
numbers.add(3);
numbers.add(5);
numbers.add(9);

numbers.remove(3);
System.out.println(numbers);

numbers.remove(Integer.valueOf(3));
System.out.println(numbers);
Output
[8, 3, 5]
[8, 5]

numbers.remove(3) removed the value at index 3, the 9. Integer.valueOf(3) is the number 3 as an Integer, the type of the list's values, so the second call looks for the value 3. (You've used Integer.valueOf on typed text; given an int, it works too.) contains and indexOf have no index kind, so numbers.contains(3) always looks for the value.

Don't remove inside a for-each

A for-each loop doesn't allow its list to grow or shrink while it runs. When it moves on to the next value, it notices that the size changed and stops the program:

import java.util.ArrayList;

public class Main {
    public static void main(String[] args) {
        ArrayList<Integer> readings = new ArrayList<>();
        readings.add(12);
        readings.add(-1);
        readings.add(15);
        readings.add(9);

        for (int reading : readings) {
            if (reading < 0) {
                readings.remove(Integer.valueOf(reading));
            }
        }
        System.out.println(readings);
    }
}
The program crashes with
Exception in thread "main" java.util.ConcurrentModificationException
	at Main.main(Main.java:11)

Line 11 is the for-each line: "concurrent modification" means the list was changed while the loop was going through it. (Adding to the list inside the loop does the same. A removal in the second-to-last round goes unnoticed: the loop just ends early and never looks at the last value, which is no better.)

To remove values while going through a list, use an index loop that goes backwards:

ArrayList<Integer> readings = new ArrayList<>();
readings.add(12);
readings.add(-1);
readings.add(-4);
readings.add(9);

for (int i = readings.size() - 1; i >= 0; i--) {
    if (readings.get(i) < 0) {
        readings.remove(i);
    }
}
System.out.println(readings);
Output
[12, 9]

Going from the end, a removal only moves values you've already checked. Going forwards with i++, the value after a removed one would move into its place, and the loop would step right past it: here the -4 would be skipped.

Your task

Challenge 1 of 3

The program reads prices until 0, and should remove every price above 50 from the list. It crashes, because it removes inside a for-each loop. Rewrite the removing loop as an index loop that goes backwards. For the input 20, 75, 35, 60, 90 and 10 it prints:

Within budget: [20, 35, 10]

Challenge 2

What does this program print? Check each remove: is it given an index or a value?

Challenge 3

The list taken holds the numbers of the taken spots in a parking garage. Read the spot numbers of cars that leave until 0, and remove each one from the list by its value. Then print what's still taken. For the input 3 and 25 the program prints:

Still taken: [12, 7, 1]