如何避免 ArrayIndexOutOfBoundsException 或 IndexOutOfBoundsException?

How can I avoid ArrayIndexOutOfBoundsException or IndexOutOfBoundsException?

如果您的问题是 我的代码中出现 java.lang.ArrayIndexOutOfBoundsException,但我不明白为什么会这样,这是什么意思,我该如何避免?

This is meant to be the most comprehensive canonical collection of information on this java.lang.ArrayIndexOutOfBoundsException topic as well as the java.lang.IndexOutOfBoundsException.

有很多这样的问题,所有这些问题要么都是含糊不清的无代码答案,要么大多是非常具体的,只针对手头的问题,没有解决根本原因,而根本原因在所有问题中都是完全相同的案例。


If you see one that falls under this general case, rather than answer it with more duplicate specialized content, mark it as a duplicate of this one.

什么是java.lang.ArrayIndexOutOfBoundsException/java.lang.IndexOutOfBoundsException?

JavaDoc 简明扼要地说:

Thrown to indicate that an array has been accessed with an illegal index. The index is either negative or greater than or equal to the size of the array.

是什么原因造成的?

This exception means that you have tried to access an index in an array or array backed list and that index does not exist.

Java uses 0 based indexes. That means all indexes start with 0 as the index of the first element if it contains any elements.

IndexOutOfBoundsException 消息非常明确,通常采用以下形式:

java.lang.IndexOutOfBoundsException: Index: 1, Size: 1

其中 Index 是您请求的不存在的索引,Size 是您索引到的结构的长度。

如您所见,Size: 1 表示唯一有效的索引是 0,而您要的是索引 1.

处的内容

For example, if you have an raw Array of objects or primitive types the valid indexes are 0 to .length - 1, in the following example the valid indexes would be 0, 1, 2, 3,.

final String days[] { "Sunday", "Monday", "Tuesday" }
System.out.println(days.length); // 3
System.out.println(days[0]); // Sunday
System.out.println(days[1]); // Monday
System.out.println(days[2]); // Tuesday
System.out.println(days[3]); // java.lang.ArrayIndexOutOfBoundsException

这也适用于 ArrayList 以及可能由 Array 支持并允许直接访问索引的任何其他 Collection 类。

如何避免java.lang.ArrayIndexOutOfBoundsException/java.lang.IndexOutOfBoundsException

直接通过索引访问时:

This uses Guava to convert the raw primitive int[] array to an ImmutableList<Integer>. Then it uses the Iterables class to safely get the value at a particular index and provides a default value when that index does not exist. Here I chose -1 to indicate an invalid index value.

final List<Integer> toTen = ImmutableList.copyOf(Ints.asList(ints));
System.out.println(Iterables.get(toTen, 0, -1));
System.out.println(Iterables.get(toTen, 100, -1));

如果您由于某种原因不能使用 Guava,可以很容易地使用您自己的函数来做同样的事情。

private static <T> T get(@Nonnull final Iterable<T> iterable, final int index, @Nonnull final T missing)
{
    if (index < 0) { return missing; }
    if (iterable instanceof List)
    {
        final List<T> l = List.class.cast(iterable);
        return l.size() <= index ? l.get(index) : missing;
    }
    else
    {
        final Iterator<T> iterator = iterable.iterator();
        for (int i = 0; iterator.hasNext(); i++)
        {
            final T o = iterator.next();
            if (i == index) { return o; }
        }
        return missing;
    }
}

迭代时:

Here is the idiomatic ways to iterate over a raw Array if you need to know the index and the value:

This is susceptible to one off errors which are the primary causes of an java.lang.ArrayIndexOutOfBoundsException:

使用传统的for-next循环:

final int ints[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (int i = 0; i < ints.length; i++)
{
    System.out.format("index %d = %d", i, ints[i]);
}

使用增强的 for-each 循环:

Here is the idiomatic way to iterate over a raw Array with the enhanced for loop if you do not need to know the actual index:

for (final int i : ints)
{
    System.out.format("%d", i);
    System.out.println();
}

使用类型安全的 Iterator:

Here is the safe way to iterate over a raw Array with the enhanced for loop and track the current index and avoids the possibility of encountering an java.lang.ArrayIndexOutOfBoundsException.

This uses Guava to easily convert the int[] to something Iterable every project should include it.

final Iterator<Integer> it = Ints.asList(ints).iterator();
for (int i = 0; it.hasNext(); i++)
{
    System.out.format("index %d = %d", i, it.next());
}

如果您不能使用 Guava 或您的 int[] 很大,您可以这样滚动自己的 ImmutableIntArrayIterator

public class ImmutableIntArrayIterator implements Iterator<Integer>
{
    private final int[] ba;
    private int currentIndex;

    public ImmutableIntArrayIterator(@Nonnull final int[] ba)
    {
        this.ba = ba;
        if (this.ba.length > 0) { this.currentIndex = 0; }
        else { currentIndex = -1; }
    }

    @Override
    public boolean hasNext() { return this.currentIndex >= 0 && this.currentIndex + 1 < this.ba.length; }

    @Override
    public Integer next()
    {
        this.currentIndex++;
        return this.ba[this.currentIndex];
    }

    @Override
    public void remove() { throw new UnsupportedOperationException(); }
}

并使用与 Guava 相同的代码。

如果您绝对必须拥有项目的序号,那么以下是最安全的方法。

// Assume 'los' is a list of Strings
final Iterator<String> it = los.iterator();
for (int i = 0; it.hasNext(); i++)
{
    System.out.format("index %d = %s", i, it.next());
}

此技术适用于所有 Iterables。它不是 index 解析,但它确实为您提供了迭代中的当前位置,即使对于没有本机 index 的事物也是如此。

最安全的方法:

The best way is to always use ImmutableLists/Set/Maps from Guava as well:

final List<Integer> ili = ImmutableList.copyOf(Ints.asList(ints));
final Iterator<Integer> iit = ili.iterator();
for (int i = 0; iit.hasNext(); i++)
{
    System.out.format("index %d = %d", i, iit.next());
}

总结:

  1. 使用原始数组很难处理,在大多数情况下应避免使用。他们有时容易受到微妙的影响 one-off errors which have plague new programmers even back to the days of BASIC

  2. 现代 Java 习语使用正确的类型安全 collections 并避免使用原始 array 结构如果在一切皆有可能。

  3. 现在几乎所有情况下都首选不可变类型。

  4. Guava 是现代 Java 开发不可或缺的工具包。

参考 Java 文档 class ArrayIndexOutOfBoundsException,当您尝试访问负数或大于的元素时会抛出此异常数组的大小。

考虑数组中有 10 个项目的情况。您可以向系统询问数组中的第一个到第十个项目。如果您尝试请求数组中的 -1 项或第 100 项,Java 将以上述异常响应。

现在记住 Java 中的数组是 zero-based。因此,您只能传递 0 到 9(含)的索引值。任何不在此范围内的数字都会抛出上述特定错误。

为了避免这种异常,让我们回到计算机科学的概念101, and review the concept of a loop invariant。这是一个条件,以确保您用于存储索引的变量必须满足特定条件。