Как избежать исключения ArrayIndexOutOfBoundsException или IndexOutOfBoundsException? - программирование

Как избежать исключения ArrayIndexOutOfBoundsException или IndexOutOfBoundsException?

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

Это будет самая полная Canonical коллекция информации об этом java.lang.ArrayIndexOutOfBoundsException, а также java.lang.IndexOutOfBoundsException.

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


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

4b9b3361

Ответ 1

Что такое java.lang.ArrayIndexOutOfBoundsException/java.lang.IndexOutOfBoundsException?

JavaDoc кратко заявляет:

Выброшено, чтобы указать, что массив был доступен с помощью незаконного индекс. Индекс является либо отрицательным, либо большим или равным размер массива.

В чем причина этого?

Это исключение означает, что вы пытались получить доступ к индексу в массив или массив, и этот индекс не существует.

Java использует индексы на основе 0. Это означает, что все индексы начинаются с 0 как индекс первого элемента, если он содержит какие-либо элементы.

Сообщение IndexOutOfBoundsException очень явное, обычно оно принимает форму:

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

Где Index - запрошенный вами индекс, который не существует, а Size - это длина структуры, в которую вы индексировали.

Как вы видите, Size: 1 означает единственный действительный индекс 0, и вы спрашивали, что было в индексе 1.

Например, если у вас есть raw 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;
    }
}

При повторении:

Вот идиоматические способы перебора raw Array, если вам нужно знать индекс и значение:

Это подвержено ошибкам, которые являются первичными причинами of java.lang.ArrayIndexOutOfBoundsException:

Использование традиционного для следующего цикла:
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]);  
}

Использование расширенного для каждого цикла:

Вот идиоматический способ итерации по raw Array с помощью расширенный для цикла, если вам не нужно знать фактический индекс:

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

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

Вот безопасный способ итерации над необработанным Array с расширенным для цикла и отслеживания текущего индекса и избегает возможности столкнувшись с 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());
}

Если вы не можете использовать Guava или ваш 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/Карты от Guava as а также:

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

Резюме:

  • Использование raw Array с трудом работать и в большинстве случаев его следует избегать. Они восприимчивы к иногда тонким одному из ошибок, у которых есть новые программисты, даже в те дни BASIC
  • Современные идиомы Java используют безопасный тип Collections и избегают использования исходных структур Array, если это вообще возможно.
  • Immutable Типы предпочтительны почти во всех случаях.
  • Guava - незаменимый инструментарий для современной разработки Java.

Ответ 2

Ссылаясь на документацию Java https://docs.oracle.com/javase/7/docs/api/java/lang/ArrayIndexOutOfBoundsException.html, это исключение возникает при попытке получить доступ к элементу, который является либо отрицательным, либо большим, чем размер массива.

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

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

Вот ссылка на инвариант цикла https://en.wikipedia.org/wiki/Loop_invariant