Как избежать ArrayIndexOutOfBoundsException или IndexOutOfBoundsException?

Если ваш вопрос, я получаю java.lang.ArrayIndexOutOfBoundsExceptionв моем коде и я не понимаю, почему это происходит. Что это значит и как я могу избежать этого?

Предполагается, что это самый полный канонический сбор информации об этом java.lang.ArrayIndexOutOfBoundsException тема, а также java.lang.IndexOutOfBoundsException,

Есть много вопросов, подобных этому, и все они имеют либо неопределенные ответы без кода, либо в основном они чрезвычайно специфичны и локализованы для рассматриваемого вопроса и не обращаются к основной причине, которая во всех случаях одинакова.


Если вы видите тот, который подпадает под этот общий случай, а не отвечаете на него более дублированным специализированным контентом, отметьте его как дубликат этого.

2 ответа

Решение

Что такое 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?

При доступе напрямую по индексу:

Это использует Гуаву, чтобы преобразовать сырой примитив 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();
}

Использование безопасного типа Iterator:

Вот безопасный способ перебрать сырой 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(); }
}

И используйте тот же код, что и в случае с 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,

Самый безопасный способ:

Лучший способ - всегда использовать ImmutableLists / Set / Maps из Guava:

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 с ними трудно работать, и их следует избегать в большинстве случаев. Они подвержены иногда тонким ошибкам, которые преследуют новых программистов даже во времена BASIC
  2. Современные идиомы Java используют правильный тип safe Collections и избегать использования сырья Array структуры, если это вообще возможно.
  3. Immutable типы сейчас предпочтительны почти во всех случаях.
  4. Guava является незаменимым инструментарием для современной разработки Java.

Ссылаясь на документацию Java класса ArrayIndexOutOfBoundsException, это исключение выдается, когда вы пытаетесь получить доступ к элементу, который либо отрицателен, либо превышает размер массива.

Рассмотрим случай, когда у вас есть 10 элементов в массиве. Вы можете спросить систему, что является первым с десятым элементом в массиве. Если вы попытаетесь спросить, что является элементом -1 или сотым элементом в массиве, Java ответит с вышеупомянутым исключением. Теперь имейте в виду, что Array в Java имеет индекс 0. Следовательно, вы можете передавать только значение индекса от 0 до 9 включительно. Любое число, не входящее в этот диапазон, выдаст конкретную ошибку, упомянутую выше.

Чтобы избежать этого исключения, давайте вернемся к концепции CS101 и рассмотрим концепцию инварианта цикла. Это условие, чтобы убедиться, что переменная, которую вы используете для хранения индекса, должна соответствовать определенному условию.

Вот ссылка на инвариант цикла: wikipedia> Инвариант цикла

Другие вопросы по тегам