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 thejava.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.
Answer
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 with0
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 are0
to.length - 1
, in the following example the valid indexes would be0,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
. Then it uses theIterables
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 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 get(@Nonnull final Iterable iterable, final int index, @Nonnull final T missing)
{
if (index < 0) { return missing; }
if (iterable instanceof List)
{
final List l = List.class.cast(iterable);
return l.size() <= index ? l.get(index) : missing;
}
else
{
final Iterator 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 anjava.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 anjava.lang.ArrayIndexOutOfBoundsException
.
This uses Guava to easily convert the
int[]
to somethingIterable
every project should include it.
final Iterator 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
{
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 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 ili = ImmutableList.copyOf(Ints.asList(ints));
final Iterator iit = ili.iterator();
for (int i = 0; iit.hasNext(); i++)
{
System.out.format("index %d = %d", i, iit.next());
}
Summary:
- 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 ofBASIC
- Modern Java idioms use proper type safe
Collections
and avoid using rawArray
structures if at all possible. Immutable
types are preferred in almost all cases now.Guava
is an indispensable toolkit for modern Java development.
No comments:
Post a Comment