我如何避免ArrayIndexOutOf边界异常或IndexOutOf边界异常?

如果您的问题是我的代码中出现java.lang.ArrayIndexOutOfBoundsException,而我不明白为什么会发生这种情况,它意味着什么,我如何才能避免它?

这是最全面的canonical信息收集 java.lang.ArrayIndexOutOfBoundsException主题以及java.lang.IndexOutOfBoundsException

类似的问题有很多,而且所有问题都有模糊的无代码答案,或者大多数问题都非常具体和局限于手头的问题,没有解决在所有情况下都完全相同的根本原因。


如果您看到属于此一般情况的案例,而不是使用更多重复的专业内容来回答它,请将其标记为此案例的副本。


解决方案

什么是java.lang.ArrayIndexOutOf边界异常/java.lang.IndexOutOf边界异常?

JavaDoc简略说明:

抛出以指示已使用非法的 索引。索引为负值或大于或等于 数组的大小。

发生这种情况的原因是什么?

此异常表示您试图访问 数组或数组支持的列表,并且该索引不存在。

Java使用基于0的索引。这意味着所有索引都以0AS开头 第一个元素的索引(如果它包含任何元素)。

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

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

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

如您所见,Size: 1表示唯一有效的索引是0,您要求的是索引1处的内容。

例如,如果您有对象或基元类型的原始Array 有效索引是0.length - 1,在以下示例中,有效索引是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

直接按索引访问时:

此操作使用Guava将原始原语int[]数组转换为 ImmutableList<Integer>。然后它使用Iterables类来安全地 获取特定索引处的值,并在以下情况下提供默认值 该索引不存在。在这里,我选择-1表示无效 索引值。

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

迭代时:

如果需要,以下是迭代原始Array的惯用方法 要了解索引和值,请执行以下操作:

这容易受到主要原因的一次性错误的影响 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循环:

以下是使用以下命令迭代原始Array的惯用方法 如果不需要知道实际索引,则增强FOR循环:

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

使用类型安全迭代器<;T>;:

下面是使用增强的迭代原始Array的安全方法 for循环并跟踪当前索引,避免 遇到java.lang.ArrayIndexOutOfBoundsException

这使用芭乐轻松地将int[]转换为Iterable 每个项目都应包括它。

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

如果您不能使用芭乐或者您的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(); }
}

并使用与芭乐相同的代码。

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

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

此技术适用于所有迭代。它不是索引分析,但它确实为您提供迭代中的当前位置,即使对于没有本机索引的内容也是如此。

最安全的方式:

最好的方法是始终使用来自番石榴的ImmutableLists/Set/Maps作为 嗯:

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的影响,这甚至可以追溯到BASIC

    的日子
  2. 现代Java习惯用法使用正确的类型安全集合,并尽可能避免使用原始数组结构。

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

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

相关文章