1. Overview
In this short tutorial, we’ll learn how to avoid IndexOutOfBoundsException when working with the subList() method provided by the List interface.
Initially, we’ll discuss how subList() works. Furthermore, we’ll see how to reproduce and fix the exception in practice.
2. Understanding the Exception
Typically, the Collections API provides the subList(int fromIndex, int toIndex) method to return a portion of the given list. The returned portion contains all the elements between the specified fromIndex and toIndex.
We should keep in mind that fromIndex is always inclusive, unlike toIndex which is exclusive.
The particularity of the subList() method is that it throws IndexOutOfBoundsException to signal that there is something wrong with the given fromIndex and endIndex.
In short, IndexOutOfBoundsException means out of range and it’s thrown when fromIndex is strictly less than zero, strictly greater than toIndex, or when toIndex is greater than the length of the list.
3. Practical Example
Now, let’s see the subList() method in action. To make things simple, let’s create a test that reproduces IndexOutOfBoundsException:
@Test
void whenCallingSuListWithIncorrectIndexes_thenThrowIndexOutOfBoundsException() {
List<String> cities = List.of("Tokyo", "Tamassint", "Paris", "Madrid", "London");
assertThatThrownBy(() -> cities.subList(6, 10)).isInstanceOf(IndexOutOfBoundsException.class);
}
As we can see, the test fails with IndexOutOfBoundsException because we provide 10 as the toIndex parameter which is greater than the length of our list.
4. Solving the Exception
The easiest way to fix the exception is to add verifications on the specified fromIndex and toIndex before calling the subList() method:
static List<String> safeSubList(List<String> myList, int fromIndex, int toIndex) {
if (myList == null || fromIndex >= myList.size() || toIndex <= 0 || fromIndex >= toIndex) {
return Collections.emptyList();
}
return myList.subList(Math.max(0, fromIndex), Math.min(myList.size(), toIndex));
}
As we see above, our alternative method is exception-safe. The idea here is to return an empty list if the given fromIndex is greater than or equal to either the list size or the given toIndex. Similarly, we return an empty list as well if the specified toIndex is less than or equal to 0.
Furthermore, if fromIndex is less than 0, then we pass 0 as the fromIndex parameter. On the other hand, if the passed toIndex parameter is greater than the list’s size, we set the list size as the new toIndex.
Now, let’s add another test case to verify that our new method works:
@Test
void whenCallingSafeSuList_thenReturnData() {
List<String> cities = List.of("Amsterdam", "Buenos Aires", "Dublin", "Brussels", "Prague");
assertThat(SubListUtils.safeSubList(cities, 6, 10)).isEmpty();
assertThat(SubListUtils.safeSubList(cities, -2, 3)).containsExactly("Amsterdam", "Buenos Aires", "Dublin");
assertThat(SubListUtils.safeSubList(cities, 3, 20)).containsExactly("Brussels", "Prague");
}
As expected, our new safe version of the subList() method can handle any specified fromIndex and toIndex.
5. Conclusion
In this short article, we discussed how to avoid IndexOutOfBoundsException when working with the subList() method.
Along the way, we learned what causes subList() to throw the exception. Then, we saw how to reproduce and fix IndexOutOfBoundsException in practice.
As always, the full source code of the examples is available over on GitHub.