Start learning free

LESSON 113 OF 671 · ARRAYS I

Slicing an ArrayList in Java with subList

Lesson Read the full explanation

TL ; DR:

list.subList(i, j) gives the items from index i up to, not including, j. Wrap it in new ArrayList<>(...) to get a new list. The original doesn't change.


List<String> letters = new ArrayList<>(List.of("a", "b", "c", "d", "e"));

List<String> middle = new ArrayList<>(letters.subList(1, 4));
// middle is [b, c, d]

// letters is still [a, b, c, d, e]


Full lesson:

Sometimes you need only part of a list, like the first few items. The subList method gives you that part.

Give it the start index and the end index. The item at the end index is not included. So letters.subList(1, 4) has the items at 1, 2 and 3.

That makes the length easy to see: 4 - 1 = 3 items.

To go to the very end, use the size as the end index:

List<String> tail = new ArrayList<>(letters.subList(3, letters.size()));
// tail is [d, e]

On its own, subList doesn't copy anything. It is a window onto the same items, so changing it changes the original too.

So wrap it in new ArrayList<>(...). That copies the items into a new list of its own. The original list stays as it was.


Your task

runners holds a race's runners, in the order they finished.

Put the first three in a new ArrayList named podium. Put everyone else, from index 3 to the end, in one named rest.

The code below prints both, then the length of runners.

Expected output:

Ana Ben Cleo
Dan Eve
5
STEP 1 OF 2
    Ctrl + Enter to check

    JAVA · ARRAYS I

    Array Slicing in Java, explained

    Taking part of a list

    Sometimes only part of a list is needed, such as the first few items. Java has no slice syntax like list[1:4]; the method for it is subList(from, to). It returns the items from index from up to, but not including, index to.

    List<String> letters = new ArrayList<>(List.of("a", "b", "c", "d", "e"));
    
    System.out.println(letters.subList(1, 4));   // [b, c, d]
    

    The end index is left out, so the length is easy to read off: 4 - 1 is 3 items, the ones at indexes 1, 2 and 3. Two edge cases follow from the rule: subList(2, 2) is an empty list, and to reach the very end you pass the size as the end index:

    List<String> tail = new ArrayList<>(letters.subList(3, letters.size()));
    System.out.println(tail);   // [d, e]
    

    subList is a window, not a copy

    The list that subList returns does not hold its own items. It is a view of the same items in the original list, so changing one changes the other:

    List<String> view = letters.subList(1, 4);
    view.set(0, "X");
    System.out.println(letters);   // [a, X, c, d, e]
    

    To get a list of your own, copy the view into a new ArrayList: new ArrayList<>(letters.subList(1, 4)). Changes to the copy leave the original alone. A view also stops being safe to use if the original list changes in size: after letters.add("f"), asking an older view for its size() throws ConcurrentModificationException. Copying avoids both problems.

    Slices from the end

    There are no negative indexes in Java, so the last items are counted from the size. The last two items of letters are letters.subList(letters.size() - 2, letters.size()), which gives [d, e]. Both ends are computed from size(), so the same line works for a list of any length, as long as it has at least two items.

    Bad ranges throw

    An end index past the size throws IndexOutOfBoundsException (on a five-item list, subList(2, 9) fails with toIndex = 9). A start after the end throws IllegalArgumentException (fromIndex(3) > toIndex(1)).

    The exercise

    The list runners holds the finishers of a race in order. The first three go on the podium, and everyone else goes in rest.

    1. Podium: copy runners.subList(0, 3), the items at indexes 0, 1 and 2, into a new list called podium.
    2. Rest: copy runners.subList(3, runners.size()) into rest. The size is one past the last index, so this runs to the end.
    List<String> runners = new ArrayList<>(List.of("Ana", "Ben", "Cleo", "Dan", "Eve"));
    List<String> podium = new ArrayList<>(runners.subList(0, 3));
    List<String> rest = new ArrayList<>(runners.subList(3, runners.size()));
    System.out.println(podium.get(0) + " " + podium.get(1) + " " + podium.get(2));
    System.out.println(rest.get(0) + " " + rest.get(1));
    System.out.println(runners.size());
    

    Output:

    Ana Ben Cleo
    Dan Eve
    5
    

    The last line shows that runners still holds all five names: the copies did not take anything out of it.

    Common mistakes

    • Off by one on the end index. On the runners list, subList(0, 2) gives only [Ana, Ben], and subList(0, 4) gives four names. To get the first three, the end index is 3.
    • Ending at size() - 1 by habit. The last valid index is size() - 1, but because the end is excluded, subList(3, runners.size() - 1) gives only [Dan] and drops Eve. Use size() to reach the end.
    • Skipping the copy. Without new ArrayList<>(...), editing podium or rest would edit runners as well.

    Key takeaways

    • subList(i, j) covers indexes i to j - 1; the end is excluded.
    • Use list.size() as the end to go to the end of the list.
    • subList is a view onto the original; wrap it in new ArrayList<>(...) for an independent copy.
    • An end beyond the size, or a start after the end, throws an exception.
    Keep going with Java500+ step-by-step lessons, from your first line of code to coding interviews. Free to start.
    Start learning free