Problem overview
The method dropEnds receives a list of integers nums and returns a new list with every number except the first and the last. The numbers in between keep their order. The list always has two to ten numbers, and with exactly two numbers the result is empty.
For [4, 8, 15, 16, 23] the answer is [8, 15, 16]. For [9, 0, 9] it is [0], and for [1, 2] it is [].
Approach
Dropping the first number means starting at index 1. Dropping the last means stopping before the last index, which is nums.size() - 1. Since subList(from, to) excludes its end index, nums.subList(1, nums.size() - 1) is exactly the numbers in between.
With two numbers the call becomes subList(1, 1): start and end are the same, which is a valid empty range. That is why the "two numbers" case needs no extra code.
As in any subList task, the result is only a window onto nums. Copying it into a new ArrayList gives the independent list the task asks for.
Step by step
- Take the window from index 1 to
nums.size() - 1withsubList. - Copy it with
new ArrayList<>(...)into aList<Integer>namedmiddle. - Return
middle.
The solution in Java
class Solution {
public List<Integer> dropEnds(List<Integer> nums) {
List<Integer> middle = new ArrayList<>(nums.subList(1, nums.size() - 1));
return middle;
}
}
Details worth knowing:
nums.size() - 1is the index of the last element, and because the end of asubListis not included, that element is left out.- The copy makes
middleindependent ofnums: changing one never changes the other.
Edge cases and common mistakes
- Stopping at
nums.size().subList(1, nums.size())removes only the first number and keeps the last. - Starting at 0.
subList(0, nums.size() - 1)removes only the last number. - Returning the view. Without the
new ArrayList<>(...)copy the result is tied tonums. - A list with fewer than two numbers. The lesson's tests never use one, but a one-item list makes the call
subList(1, 0), which throwsIllegalArgumentException(fromIndex(1) > toIndex(0)).
Checking the numbers
It helps to test the two ends separately. For [3, 8, 1, 9, 4]: subList(1, nums.size()) gives [8, 1, 9, 4] (the first number is gone, the last is still there), and subList(0, nums.size() - 1) gives [3, 8, 1, 9] (the opposite). Only subList(1, nums.size() - 1) gives [8, 1, 9]. And for a two-item list, subList(1, 1) has size 0.
Tracing the examples
[4, 8, 15, 16, 23]has 5 numbers, so the window runs from index 1 to index 4 (excluded): the numbers 8, 15 and 16.[9, 0, 9]has 3 numbers, so the window runs from 1 to 2 (excluded): only the 0. It does not matter that both ends are equal; the method works on positions, not on values.[1, 2]has 2 numbers, so the window runs from 1 to 1 (excluded), which is empty.
Why the copy is worth it
A view stays connected to nums in more ways than one. If the original list grows or shrinks after the view was made, using the view throws a ConcurrentModificationException. A caller who gets back a view of its own list can run into that much later and far from the cause. A copy is a plain list that nobody else holds, so the caller can do anything with it. The cost is one extra pass over at most ten numbers, which is nothing.
Testing
Inputs and the output of the solution:
[4, 8, 15, 16, 23]gives8 15 16.[9, 0, 9]gives0.[1, 2]gives an empty list.
