How to avoid ArrayIndexOutOfBoundsException or IndexOutOfBoundsException? [duplicate]

元气小坏坏 提交于 2019-11-25 22:16:02

问题


This question already has an answer here:

  • What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it? 25 answers

If your question is I am getting a java.lang.ArrayIndexOutOfBoundsException in my code and I do not understand why it is happening. What does it mean and how can I avoid it?

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.

There are many questions like this and all of them have either vague no code answers, or mostly they are extremely specific and localized to the question at hand and do not address the root cause which is exactly the same in all cases.


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.


回答1:


What is java.lang.ArrayIndexOutOfBoundsException / java.lang.IndexOutOfBoundsException?

The JavaDoc curtly states:

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.

What causes it to happen?

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.

The IndexOutOfBoundsException message is very explicit, it usually takes the form of:

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

Where Index is the index that you requested that does not exist and Size is the length of the structure you were indexing into.

As you can see a Size: 1 means the only valid index is 0 and you were asking for what was at index 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

This also applies to ArrayList as well as any other Collection classes that may be backed by an Array and allow direct access to the the index.

How to avoid the java.lang.ArrayIndexOutOfBoundsException / java.lang.IndexOutOfBoundsException?

When accessing directly by index:

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

If you can't use Guava for some reason it is easy to roll your own function to do this same thing.

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

When iterating:

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:

Using a traditional for/next loop:
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]);  
}

Using an enhanced for/each loop:

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

Using a type safe 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());
}

If you can not use Guava or your int[] is huge you can roll your own ImmutableIntArrayIterator as such:

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

And use the same code as you would with Guava.

If you absolutely must have the ordinal of the item the following is the safest way to do it.

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

This technique works for all Iterables, it is not an index perse but it does give you the current position in the iteration even for things that do not have a native index.

The safest way:

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

Summary:

  1. Using raw Array are difficult to work with and should be avoided in most cases. They are susceptible to sometimes subtle one off errors which have plague new programmers even back to the days of BASIC
  2. Modern Java idioms use proper type safe Collections and avoid using raw Array structures if at all possible.
  3. Immutable types are preferred in almost all cases now.
  4. Guava is an indispensable toolkit for modern Java development.



回答2:


Referring to the Java documentation Class ArrayIndexOutOfBoundsException , this exception is thrown when you try to access an element that is either negative, or greater than the size of the Array.

Consider the case you have 10 items in the array. You can ask the system what is the first to the tenth item within the array. If you try to ask what is the -1 item or the 100th item in the array, Java would response with the above exception. Now keep in mind that Array in Java is 0 indexed. Therefore, you can only pass an index value of 0 to 9 inclusive. Any number not in this range will throws the specific error mentioned above.

To avoid this exception, let's go back to the concept of CS101, and review the concept of loop invariant. This is a condition to make sure that the variable you use to store the index must be meet the particular condition.

Here is a link to loop invariant: wikipedia > Loop invariant



来源:https://stackoverflow.com/questions/32568261/how-to-avoid-arrayindexoutofboundsexception-or-indexoutofboundsexception

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!