Search code examples
javaarraylistindexoutofboundsexceptiondefensive-programming

Why doesn't the rangeCheck method in the java.util.ArrayList class check for negative index?


/**
 * Checks if the given index is in range.  If not, throws an appropriate
 * runtime exception.  This method does *not* check if the index is
 * negative: It is always used immediately prior to an array access,
 * which throws an ArrayIndexOutOfBoundsException if index is negative.
 */
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

From: jdk/ArrayList.java at jdk8-b120 · openjdk/jdk · GitHub

If we write the following code, both indexes are out of bounds, but the exception types are different.

import java.util.ArrayList;
import java.util.List;

public class Test {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("");

        try {
            list.get(-1);
        } catch (Exception e) {
            e.printStackTrace();
        }

        try {
            list.get(1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

The output is as follows:

java.lang.ArrayIndexOutOfBoundsException: -1
    at java.util.ArrayList.elementData(ArrayList.java:424)
    at java.util.ArrayList.get(ArrayList.java:437)
    at Test.main(Test.java:11)
java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.ArrayList.rangeCheck(ArrayList.java:659)
    at java.util.ArrayList.get(ArrayList.java:435)
    at Test.main(Test.java:17)

Related question:

  • Why does rangeCheckForAdd method in the java.util.ArrayList check for negative index?
  • Why doesn't java.util.Arrays.ArrayList do index out-of-bounds checking?

What confuses me is why their implementations are inconsistent? Are these methods written by different people with their own programming style? In other words, if out-of-bounds exceptions will eventually fire, then there is no need to check.


Solution

  • It's a micro-optimization. For code clarity you might prefer the same exception for both, but when you're in a hot loop you'll want to avoid an unnecessary operation. ArrayList being an old class, the effect this has may have varied between times and JDK versions. If someone has enough interest they could benchmark it with 1.8 and newer JDKs to see how much of an optimization it is for get().

    Since accessing a negative array index will fail anyway, there is no need to check for it. However the size of the ArrayList is not always the same as the size of its internal array, so it needs to be checked explicitly.

    As to why rangeCheckForAdd does check for negative indexes, good question. Adding is slow anyway, so the micro-optimization wouldn't make much of a difference. Maybe they wanted consistent error messaging here.