No Pop on a list
In some languages, a method named pop removes the last item of an array and hands it back. C#'s List<T> has no such method. Instead it has methods that remove by position or by value, and removing the last item is just the case where the position is the final one.
The two methods to know are:
RemoveAt(index)deletes the item at a position.Remove(value)deletes the first item that equals a value.
The lesson needs the first one.
RemoveAt and the last index
List indexes start at 0, and a list with Count items has valid indexes 0 to Count - 1. So the last item is always at Count - 1:
List<int> numbers = new List<int> { 1, 4, 6 };
numbers.RemoveAt(numbers.Count - 1);
Console.WriteLine(string.Join(", ", numbers)); // 1, 4
numbers.RemoveAt(0);
Console.WriteLine(string.Join(", ", numbers)); // 4
Everything after the removed item shifts one place to the left, and Count drops by one. That is why the same expression numbers.Count - 1 points at a different item on the next call: it always means "the current last item".
Count is a property, so it takes no parentheses. Arrays use Length, but a list uses Count.
Remove is not RemoveAt
This is the trap of the topic. Remove takes a value, not an index, and it returns a bool that says whether it found something to remove:
List<int> list = new List<int> { 1, 4, 6, 1 };
Console.WriteLine(list.Remove(1)); // True
Console.WriteLine(string.Join(", ", list)); // 4, 6, 1
Console.WriteLine(list.Remove(99)); // False
Here Remove(1) deleted the first item equal to 1, not the item at index 1, and it left the second 1 alone. On a List<int> the two methods are easy to mix up because both take an int. If you have a position, call RemoveAt.
Getting the value before you remove it
RemoveAt returns nothing. If you want the removed item, as pop would give you, read it first:
List<string> names = new List<string> { "Ann", "Bo", "Cy" };
string last = names[names.Count - 1];
names.RemoveAt(names.Count - 1);
Console.WriteLine(last); // Cy
Console.WriteLine(string.Join(", ", names)); // Ann, Bo
Removing the first item, and many items
The same method handles the front of the list. RemoveAt(0) drops the first item. Removing from the end is cheap, because nothing after it has to move. Removing from the front or the middle is slower on a long list, since every later item shifts left by one place.
Because the removed item is gone for good, decide whether you need its value before the call, not after it.
To clear a whole list, call Clear(). To remove a run of items, RemoveRange(start, count) takes the first index and how many items to delete.
The exercise, step by step
The lesson starts with these lists:
List<string> fruits = new List<string>(new[] {"mango", "kiwi", "grapefruit"});
List<int> ages = new List<int>(new[] {14, 32, 50, 21});
- Remove the last fruit. Call
fruits.RemoveAt(fruits.Count - 1);. The last index isCount - 1, which is 2 here, so"grapefruit"goes andmango, kiwiremain. - Remove two ages. Call
ages.RemoveAt(ages.Count - 1);twice. The first call removes 21, which shrinks the list to three items. The same expression now points at 50, so the second call removes that. The list ends as14, 32.
The complete solution is:
List<string> fruits = new List<string>(new[] {"mango", "kiwi", "grapefruit"});
List<int> ages = new List<int>(new[] {14, 32, 50, 21});
fruits.RemoveAt(fruits.Count - 1);
ages.RemoveAt(ages.Count - 1);
ages.RemoveAt(ages.Count - 1);
Common mistakes
- Removing from an empty list. With
Countequal to 0, the indexCount - 1is -1, andRemoveAtthrows anArgumentOutOfRangeException. CheckCount > 0first when the list might be empty. - Using
Countinstead ofCount - 1. The indexCountis one past the end, so it is out of range and throws. - Calling
Removewhen you meanRemoveAt.Remove(2)looks for the value 2, not position 2. - Using a fixed index.
RemoveAt(3)works for a four-item list only. The expressionCount - 1keeps working as the list changes. - Expecting the removed value back.
RemoveAtis a statement, not an expression with a result.
Key takeaways
List<T>has noPop; useRemoveAt(list.Count - 1)to drop the last item.RemoveAttakes an index.Removetakes a value and removes its first match.- Items after a removed one shift left, and
Countgoes down by one. - Read the item before removing it when you need its value.