-3
/**
 * 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.

Poison
  • 389
  • 2
  • 14
  • what is ArrayList.rangeCheckForAdd() ?? – ControlAltDel Feb 28 '22 at 13:22
  • what is java.util.Arrays.ArrayList ?? – ControlAltDel Feb 28 '22 at 13:22
  • In any case. ArrayList's implementation is how how the programming team at Sun / Oracle chose to do it. It's honestly as simple an answer as that – ControlAltDel Feb 28 '22 at 13:23
  • Note that `ArrayIndexOutOfBoundsException` **is-a** `IndexOutOfBoundsException` and the spec just requires an IOOBE to be thrown, so by not checking for negative values, one unnecessary check is avoided (this may not actually result in higher performance, because the runtime is pretty good at optimizing unnecessary range checks away). – Joachim Sauer Feb 28 '22 at 13:41
  • checking for a negative index? an index can't be negative – Stultuske Feb 28 '22 at 13:49
  • 1
    "Why doesn't ArrayList.rangeCheck() check for negative index?" The answer is in the source-code comment yourself included: "It is always used immediately prior to an array access, which throws an ArrayIndexOutOfBoundsException if index is negative." – user16320675 Feb 28 '22 at 13:50
  • @Stultuske The `rangeCheckForAdd` method in the `java.util.ArrayList` checks for negative index but the `rangeCheck` method does not. – Poison Feb 28 '22 at 13:51
  • @user16320675 So why does `rangeCheckForAdd` perform a negative index check? – Poison Feb 28 '22 at 13:53
  • 1
    I did not implement nor was I involved in the development of `raneCheckForAdd` - my comment is to answer the question in title (and its only based on posted comment) - Maybe this question should be asked in some mailing-lists of the Java developers – user16320675 Feb 28 '22 at 13:54
  • @ControlAltDel [rangeCheckForAdd](https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/java/util/ArrayList.java#L644-L647) and [java.util.Arrays.ArrayList](https://github.com/openjdk/jdk/blob/jdk8-b120/jdk/src/share/classes/java/util/Arrays.java#L3800-L3868) – Poison Feb 28 '22 at 13:55
  • `rangeCheckForAdd` is **not** always called immediately before a array access, so the comment in `rangeCheck` does not apply here. The `ForAdd` variant sometimes even causes the array to be resized, so checking for negative index here makes sense. – Joachim Sauer Feb 28 '22 at 14:32

2 Answers2

4

The range check on the array access checks if the array index is in the range 0 <= index < elementData.length.

For a java.util.ArrayList the valid range of indexes is only 0 <= index < size, with size <= elementData.length. Since the array access cannot correctly check the upper bound (size could be less then elementData.length) the ArrayList class has to make sure that the upper bound is not violated.

The lower bound doesn't need to be checked because the lower bound is always 0 and is validated when the elementData array is accessed.


Why does rangeCheckForAdd check for negative index?

When adding elements it can be necessary to grow the size of the elementData array before the new element is added. But if the index is invalid (negative or greater than size) you don't want to grow the elementData array. In this case it is therefore necessary to do the complete bounds check (0 <= index <= size) before anything else is done.


Why doesn't java.util.Arrays.ArrayList do out of bounds checking?

The size of a java.util.Arrays.ArrayList is exactly the size of the backing array. A separate bounds check (as in the of the java.util.ArrayList, where size can be smaller then the size of the backing array) is therefore not required.

Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
3

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.

Kayaman
  • 72,141
  • 5
  • 83
  • 121
  • After some searching, I found that in a JDK 9 commit, the implementation had been replaced with checking for negative numbers and using JVM method inlining. See: [8079136: Accessing a nested sublist leads to StackOverflowError · openjdk/jdk@763f489 · GitHub](https://github.com/openjdk/jdk/commit/763f4898ff9f9a3dd98787750ac8ef7a83f61ffe#diff-711c78af38e1f26c5f890f32b369c5aed745562dbdee278a1285d6cc308624a9) – Poison Mar 01 '22 at 02:11