为什么 java.util.ArrayList class 中的 rangeCheck 方法不检查负索引?

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));
}

发件人:jdk/ArrayList.java at jdk8-b120 · openjdk/jdk · GitHub

如果我们写下面的代码,两个索引都越界,只是异常类型不同

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();
        }
    }

}

输出结果如下:

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)

相关问题:

令我困惑的是为什么他们的实现不一致?这些方法是由不同的人用自己的编程风格编写的吗?换句话说,如果越界异常最终会触发,那么就不需要检查了。

这是一个 micro-optimization。为了代码清晰,您可能希望两者使用相同的异常,但是当您处于热循环中时,您会希望避免不必要的操作。 ArrayList 作为一个旧的 class,其影响可能因时间和 JDK 版本而异。如果有人有足够的兴趣,他们可以用 1.8 和更新的 JDKs 对其进行基准测试,以查看它对 get().

的优化程度

由于访问负数数组索引无论如何都会失败,因此无需检查它。但是 ArrayList 的大小并不总是与其内部数组的大小相同,因此需要明确检查。

至于为什么 rangeCheckForAdd 检查负索引,问得好。无论如何添加都很慢,所以 micro-optimization 不会有太大的不同。也许他们想要在此处提供一致的错误消息。

数组访问的范围检查检查数组索引是否在范围 0 <= index < elementData.length

对于 java.util.ArrayList,索引的有效范围仅为 0 <= index < sizesize <= elementData.length。由于数组访问无法正确检查上限(size 可能小于 elementData.length),ArrayList class 必须确保不违反上限。

不需要检查下限,因为下限总是 0 并且在访问 elementData 数组时验证。


为什么 rangeCheckForAdd 检查负索引?

添加元素时,可能需要在添加新元素之前增加 elementData 数组的大小。但是如果索引无效(负数或大于大小),您不想增加 elementData 数组。因此,在这种情况下,有必要在执行任何其他操作之前进行完整的边界检查 (0 <= index <= size)。


为什么java.util.Arrays.ArrayList不进行越界检查?

java.util.Arrays.ArrayList 的大小恰好是后备数组的大小。因此不需要单独的边界检查(如 java.util.ArrayList 中的那样,其中大小可以小于支持数组的大小)。