Adding and removing in the middle of a list
An ArrayList is not limited to growing at the end. Two methods change the list at any position: add(i, x) inserts x at index i, and remove(i) takes out the item at index i. In both cases the other items shift so that the list stays one unbroken run with no holes.
Insert with add(i, x)
add(x) with one argument puts x at the end. With an index first, the item goes in at that index, and everything from that index on moves one place to the right:
List<String> pets = new ArrayList<>(List.of("Rex", "Milo"));
pets.add(0, "Luna");
System.out.println(pets); // [Luna, Rex, Milo]
The valid indexes for add(i, x) run from 0 to size(). An index equal to the size is the end of the list, so add(2, "z") on a list of two items works and appends. Anything bigger throws an IndexOutOfBoundsException; on a two-item list, add(5, "z") fails with the message Index: 5, Size: 2.
Remove with remove(i)
remove(i) takes out the item at index i. The items after it move one place to the left to close the gap, and the method returns the item it took out:
String first = pets.remove(0);
System.out.println(first); // Luna
System.out.println(pets); // [Rex, Milo]
Here the index must be from 0 to size() - 1. Calling remove(2) on a list of two items throws IndexOutOfBoundsException with the message Index 2 out of bounds for length 2.
Indexes change after every edit
After an insert or a removal, every item behind that spot has a new index. If you plan several edits, work out the indexes against the list as it is at that moment, not as it was at the start. That is the whole point of the exercise below.
The trap: remove on a list of numbers
List<Integer> has two methods called remove: remove(int index) and remove(Object o). When you pass an int, Java picks the index version:
List<Integer> nums = new ArrayList<>(List.of(5, 2, 9, 2));
nums.remove(2); // removes the item at index 2 (the 9)
System.out.println(nums); // [5, 2, 2]
nums.remove(Integer.valueOf(2)); // removes the first value equal to 2
System.out.println(nums); // [5, 2]
To remove by value, pass an Integer object, for example with Integer.valueOf(2). With a List<String> there is no confusion: a string is never an index.
Also note that List.of(...) by itself gives a list that cannot change: add and remove on it throw UnsupportedOperationException. That is why the lesson wraps it in new ArrayList<>(...).
The exercise
The vet's line starts as Rex, Milo, Luna. Bella has an appointment and goes second, at index 1. Then Milo leaves. Because Bella went in first, Milo moved from index 1 to index 2.
- Insert Bella:
pets.add(1, "Bella"). The list is nowRex, Bella, Milo, Luna, sosize()is 4. - Remove Milo by his new index:
pets.remove(2). Using index 1 would remove Bella instead.
List<String> pets = new ArrayList<>(List.of("Rex", "Milo", "Luna"));
pets.add(1, "Bella");
System.out.println(pets.size());
pets.remove(2);
System.out.println(pets.get(0) + " " + pets.get(1) + " " + pets.get(2));
Output:
4
Rex Bella Luna
Common mistakes in the exercise
- Removing at the old index. Milo started at index 1, but after Bella goes in he is at index 2. With
pets.remove(1)the program removes Bella instead, and the list goes back to[Rex, Milo, Luna]. - Doing the steps in the wrong order. If Milo is removed first (
remove(1)) and Bella is inserted afterwards, the final list is also[Rex, Bella, Luna]. Both orders work as long as each index matches the list at that moment, which is why the lesson's index for Milo is 2: it assumes Bella is already in. - Using
add(x)when you meantadd(i, x). The one-argument form puts Bella at the end of the line.
Key takeaways
add(i, x)inserts at indexi(allowed from 0 up to the size) and shifts the rest right.remove(i)deletes the item at indexi, shifts the rest left and returns the removed item.- Recompute indexes after each edit.
- On a
List<Integer>, anintargument means an index; useInteger.valueOf(x)to remove a value.
