ArrayIndexOutOfBoundsException是什么意思,我如何摆脱它?

下面是一个触发异常的代码示例:

String[] names = { "tom", "bob", "harry" };
for (int i = 0; i <= names.length; i++) {
    System.out.println(names[i]);
}

当前回答

什么原因导致ArrayIndexOutOfBoundsException?

如果您将变量视为一个可以放置值的“盒子”,那么数组就是一系列相邻放置的盒子,其中盒子的数量是一个有限且显式的整数。

创建一个这样的数组:

final int[] myArray = new int[5]

创建一行5个方框,每个方框包含一个int。每个方框都有一个索引,即一系列方框中的位置。这个索引从0开始,结束于N-1,其中N是数组的大小(盒子的数量)。

要从这一系列框中检索一个值,你可以通过它的索引来引用它,就像这样:

myArray[3]

这将给出该系列中第4个框的值(因为第一个框的索引为0)。

ArrayIndexOutOfBoundsException异常是由于试图检索一个不存在的“盒子”,通过传递一个比最后一个“盒子”的索引更高的索引,或者是负的索引而引起的。

在我的运行示例中,这些代码片段将产生这样一个异常:

myArray[5] //tries to retrieve the 6th "box" when there is only 5
myArray[-1] //just makes no sense
myArray[1337] //way to high

如何避免ArrayIndexOutOfBoundsException

为了防止ArrayIndexOutOfBoundsException,有一些关键点需要考虑:

循环

当循环遍历一个数组时,始终确保您正在检索的索引严格小于数组的长度(盒子的数量)。例如:

for (int i = 0; i < myArray.length; i++) {

注意<,不要把a =混进去。

你可能想做这样的事情:

for (int i = 1; i <= myArray.length; i++) {
    final int someint = myArray[i - 1]

只是不喜欢。坚持上面的一个(如果你需要使用索引),它会为你省去很多痛苦。

如果可能,请使用foreach:

for (int value : myArray) {

这样你就完全不用考虑索引了。

在循环时,无论你做什么,永远不要改变循环迭代器的值(这里:i)。它唯一应该改变值的地方是让循环继续下去。以其他方式更改它只是冒着异常的风险,并且在大多数情况下是不必要的。

检索/更新

当检索数组的任意元素时,总是检查它是一个针对数组长度的有效索引:

public Integer getArrayElement(final int index) {
    if (index < 0 || index >= myArray.length) {
        return null; //although I would much prefer an actual exception being thrown when this happens.
    }
    return myArray[index];
}

其他回答

我所见过的看似神秘的arrayindexoutofboundsexception最常见的情况,即显然不是由您自己的数组处理代码引起的,是SimpleDateFormat的并发使用。特别是在servlet或控制器中:

public class MyController {
  SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");

  public void handleRequest(ServletRequest req, ServletResponse res) {
    Date date = dateFormat.parse(req.getParameter("date"));
  }
}

如果两个线程一起进入SimplateDateFormat.parse()方法,你可能会看到一个ArrayIndexOutOfBoundsException异常。注意SimpleDateFormat类javadoc的同步部分。

确保代码中没有像servlet或控制器那样以并发方式访问线程不安全类(如SimpleDateFormat)的地方。检查servlet和控制器的所有实例变量,寻找可能的可疑对象。

if (index < 0 || index >= array.length) {
    // Don't use this index. This is out of bounds (borders, limits, whatever).
} else {
    // Yes, you can safely use this index. The index is present in the array.
    Object element = array[index];
}

参见:

Java教程-语言基础-数组


更新:根据您的代码片段,

for (int i = 0; i<=name.length; i++) {

索引包含数组的长度。这是越界的。你需要用<替换<=。

for (int i = 0; i < name.length; i++) {

每当这个异常出现时,它意味着你正在尝试使用一个超出界限的数组索引,或者在外行术语中,你正在请求比你初始化的更多。

为了防止这种情况,总是要确保你没有请求一个数组中不存在的索引,即如果数组长度为10,那么你的索引必须在0到9之间

你的第一个目标应该是能够合理清晰地解释它的文档:

抛出,表示使用非法索引访问了数组。索引值为负或大于或等于数组的大小。

例如:

int[] array = new int[5];
int boom = array[10]; // Throws the exception

至于如何避免……嗯,别这么做。小心你的数组索引。

人们有时会遇到的一个问题是认为数组是1索引的,例如。

int[] array = new int[5];
// ... populate the array here ...
for (int index = 1; index <= array.length; index++)
{
    System.out.println(array[index]);
}

这将遗漏第一个元素(索引0),并在索引为5时抛出异常。这里的有效索引是0-4。正确的,地道的for语句应该是:

for (int index = 0; index < array.length; index++)

(当然,这是假设您需要索引。如果你可以使用增强的for循环,那就这样做。)

这个简单的问题就讲到这里,我只想强调Java中的一个新特性,它可以避免所有关于数组中索引的困惑,即使是初学者。Java-8为您抽象了迭代的任务。

int[] array = new int[5];

//If you need just the items
Arrays.stream(array).forEach(item -> { println(item); });

//If you need the index as well
IntStream.range(0, array.length).forEach(index -> { println(array[index]); })

有什么好处?首先是可读性,比如英语。其次,您不需要担心ArrayIndexOutOfBoundsException