如何避免ArrayIndexOutOfBoundsException或IndexOutOfBoundsException?

时间:2015-09-14 15:13:23

标签: java arrays arraylist indexoutofboundsexception

如果您的问题是,我的代码中会收到java.lang.ArrayIndexOutOfBoundsException,而我不明白为什么会这样。它是什么意思,我该如何避免呢?

  

这是关于此的最全面的Canonical信息集合   java.lang.ArrayIndexOutOfBoundsException主题以及java.lang.IndexOutOfBoundsException

有很多这样的问题,所有这些问题都没有模糊的代码答案,或者大多数都是非常具体的,并且局限于手头的问题,并没有解决所有情况下完全相同的根本原因。 / p>

  

如果你看到属于这种一般情况的那个,而不是用更多重复的专门内容回答它,请将其标记为此副本的副本。

2 个答案:

答案 0 :(得分:5)

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

JavaDoc简明扼要地陈述:

  

抛出表示已使用非法访问数组   指数。该指数为负数或大于或等于   数组的大小。

是什么导致它发生?

  

此异常表示您已尝试访问某个索引   数组或数组支持列表,该索引不存在。

     

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

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以及可能由Collection支持的任何其他Array类,并允许直接访问索引。

如何避免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();
}

使用类型安全的迭代器:

  

这是使用增强功能迭代原始Array的安全方法   for loop 并跟踪当前索引并避免可能性   遇到java.lang.ArrayIndexOutOfBoundsException

     

这使用Guava轻松将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(); }
}

使用与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 perse,但它确实为您提供迭代中的当前位置,即使对于没有本机index的内容也是如此。

最安全的方式:

  

最好的方法是始终使用来自番石榴的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. 使用原始Array很难处理,在大多数情况下应该避免使用。他们容易受到微妙的one off errors的影响,这些问题甚至可以追溯到BASIC
  2. 时代的新程序员
  3. 现代Java习语使用正确的类型安全Collections,并尽可能避免使用原始Array结构。
  4. 现在几乎在所有情况下,
  5. Immutable类型都是首选。
  6. Guava是现代Java开发不可或缺的工具包。

答案 1 :(得分:0)

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

考虑一下你在数组中有10个项目的情况。您可以询问系统数组中第一个到第十个项目的内容。如果您尝试询问数组中的-1项或第100项,Java将响应上述异常。现在请记住,Java中的Array是0索引的。因此,您只能传递0到9的索引值(包括0和9)。不在此范围内的任何数字都将引发上述特定错误。

为了避免这种异常,让我们回到CS101的概念,并回顾循环不变量的概念。这是确保用于存储索引的变量必须满足特定条件的条件。

以下是循环不变量的链接:wikipedia > Loop invariant