Poison

rangeCheck

如果看过 JDK 8 中 ArrayList 的源码,你会发现存在如下代码 ArrayList.java at jdk8-b120:

1
2
3
4
5
6
7
8
9
10
/**
* 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));
}

令我不解的是为何此处不对负数索引进行检查,有部分观点认为这是一项微优化,即负数肯定会触发异常,所以不必进行检查,但是我并不太理解该说法,因为只要越界,底层都会抛出异常,那么 rangeCheck 方法还有存在的必要吗?如果我们编写如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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();
}
}

}

你会发现输出如下:

1
2
3
4
5
6
7
8
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)

以上的异常信息让人困惑,都是越界,但是异常类型却不一样,虽然 ArrayIndexOutOfBoundsExceptionIndexOutOfBoundsException 的子类。如果查看 ArrayList 类中的 rangeCheckForAdd 方法,你会发现实现如下 ArrayList.java at jdk8-b120:

1
2
3
4
5
6
7
/**
* A version of rangeCheck used by add and addAll.
*/
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

此处对索引为负数的情况进行了检查,关于这块逻辑我也无法解释,我更相信这些方法是由不同开发人员编写,如果查看 java.util.Arrays.ArrayList 的源码,你会发现根本不存在范围检查。

在查询代码提交记录后我发现在 JDK 9 中 rangeCheck 方法已经修改为了支持检查负数索引的实现,这次提交可参考:8079136: Accessing a nested sublist leads to StackOverflowError · openjdk/jdk@763f489 · GitHub,且调用的参数检查方法 checkIndex 被标记了 @ForceInline,即强制内联。

Reference

JDK 9 intrinsics · GitHub
Difference between “native” keyword and @HotSpotIntrinsicCandidate annotation - Stack Overflow