Що викликає java.lang.ArrayIndexOutOfBoundsException і як це запобігти?


298

Що ArrayIndexOutOfBoundsExceptionозначає і як я його позбудуся?

Ось зразок коду, який запускає виняток:

String[] names = { "tom", "bob", "harry" };
for (int i = 0; i <= names.length; i++) {
    System.out.println(names[i]);
}

1
Посилаючись на останнє запитання, код буде корисним. Ви маєте доступ до масиву з відомим індексом або вам потрібно починати налагодження, щоб з'ясувати, як обчислюється індекс, коли виникає помилка?
justkt

43
Замініть i <= name.lengthна i < name.length- або краще, напишіть розширений цикл. ( for (String aName : name) { ... })
Жан Хомінал

1
це означає, що ви хочете отримати елемент масиву, який не існує, 'i <= name.length' означає, що ви хочете отримати довжину елемента + 1 - його не існує.
gbk


Відповіді:


296

Ваш перший порт дзвінка повинен бути документацією, яка пояснює це досить чітко:

Викинуто, щоб вказати на те, що до масиву звертався з незаконним індексом. Індекс або негативний, або більший, або дорівнює розміру масиву.

Так, наприклад:

int[] array = new int[5];
int boom = array[10]; // Throws the exception

Щодо того, як цього уникнути ... гм, не робіть цього. Будьте обережні зі своїми індексами масивів.

Одна з проблем, з якою люди стикаються, - це думати, що масиви є 1-індексованими, наприклад

int[] array = new int[5];
// ... populate the array here ...
for (int index = 1; index <= array.length; index++)
{
    System.out.println(array[index]);
}

Це пропустить перший елемент (індекс 0) і викине виняток, коли індекс дорівнює 5. Дійсні індекси тут 0-4 включно. Правильним, ідіоматичним forтвердженням тут було б:

for (int index = 0; index < array.length; index++)

(Це , звичайно , якщо вам потрібен індекс. Якщо ви можете використовувати розширений цикл замість цього, зробіть це.)


1
Я хотів би додати , що для багатовимірних масивів , які можуть мати довільну форму в Java, вкладені цикли повинні перевірити для відповідної довжини подмассіва: for (int nestedIndex = 0; nestedIndex < array[outerIndex].length; nestedIndex++) { ... array[outerIndex][nestedIndex] ... }.
Андрій

52
if (index < 0 || index >= array.length) {
    // Don't use this index. This is out of bounds (borders, limits, whatever).
} else {
    // Yes, you can safely use this index. The index is present in the array.
    Object element = array[index];
}

Дивись також:


Оновлення : відповідно до фрагменту коду,

for (int i = 0; i<=name.length; i++) {

Індекс включає довжину масиву. Це поза межами. Вам потрібно замінити <=на <.

for (int i = 0; i < name.length; i++) {

22

З цієї чудової статті: ArrayIndexOutOfBoundsException in for loop

Коротше кажучи:

В останній ітерації

for (int i = 0; i <= name.length; i++) {

i дорівнюватиме name.length що є незаконним індексом, оскільки індекси масиву базуються на нулі.

Ваш код повинен читати

for (int i = 0; i < name.length; i++) 
                  ^

17

Це означає, що ви намагаєтеся отримати доступ до індексу масиву, який недійсний, оскільки він не знаходиться між межами.

Наприклад, це ініціалізує примітивний цілочисельний масив з верхньою межею 4.

int intArray[] = new int[5];

Програмісти рахують від нуля. Так це, наприклад, кине як, ArrayIndexOutOfBoundsExceptionоскільки верхня межа 4, а не 5.

intArray[5];

4
Межі - це межі в межах діапазону. тобто; 0-5
Джон Джотта

11

Щоб уникнути винятку індексу масиву поза межами діапазону, слід використовувати оператор з покращеноюfor операцією, де і коли вони можуть.

Основна мотивація (і використання випадку) - це коли ви повторюєте і не потребуєте жодних складних кроків ітерації. Ви не зможете використовувати розширені- forдля переміщення назад у масиві або лише повторити всі інші елементи.

У вас гарантується, що вам не вистачить елементів, щоб повторити процес, і ваш [виправлений] приклад легко перетвориться.

Код нижче:

String[] name = {"tom", "dick", "harry"};
for(int i = 0; i< name.length; i++) {
    System.out.print(name[i] + "\n");
}

... еквівалентно цьому:

String[] name = {"tom", "dick", "harry"};
for(String firstName : name) {
    System.out.println(firstName + "\n");
}

11

Які причини ArrayIndexOutOfBoundsException?

Якщо ви думаєте про змінну як "поле", куди ви можете розмістити значення, то масив - це низка полів, розміщених поруч з іншими, де кількість коробок є кінцевим і явним цілим числом.

Створення такого масиву:

final int[] myArray = new int[5]

створює ряд з 5 ящиків, у кожному з яких є int. Кожна з коробок має покажчик, позицію в серії коробок. Цей індекс починається з 0 і закінчується на N-1, де N - розмір масиву (кількість полів).

Щоб отримати одне із значень із цієї серії вікон, ви можете посилатися на нього через його індекс, наприклад:

myArray[3]

Що дасть вам значення 4-го вікна в серії (оскільки перший ящик має індекс 0).

A ArrayIndexOutOfBoundsExceptionвикликається спробою відновити "вікно", яке не існує, передаючи індекс, що перевищує індекс останнього "вікна", або мінус.

З мого запущеного прикладу ці фрагменти коду створюють такий виняток:

myArray[5] //tries to retrieve the 6th "box" when there is only 5
myArray[-1] //just makes no sense
myArray[1337] //waay to high

Як уникнути ArrayIndexOutOfBoundsException

З метою запобігання ArrayIndexOutOfBoundsExceptionслід враховувати деякі ключові моменти:

Цикл

Під час перегляду масиву завжди переконайтеся, що індекс, який ви отримуєте, суворо менший за довжину масиву (кількість полів). Наприклад:

for (int i = 0; i < myArray.length; i++) {

Зауважте <, ніколи не змішуйте =там.

Можливо, вам захочеться спокуси зробити щось подібне:

for (int i = 1; i <= myArray.length; i++) {
    final int someint = myArray[i - 1]

Просто ні. Дотримуйтесь наведеного вище (якщо вам потрібно скористатися індексом), і це вбереже вам багато болю.

Де можливо, використовуйте foreach:

for (int value : myArray) {

Таким чином вам зовсім не доведеться думати про індекси.

Під час циклу, що б ви не робили, НІКОЛИ не змінюйте значення ітератора циклу (тут:) i. Єдине місце, яке має змінити значення, - це тримати цикл. Змінювати його в іншому випадку - це просто ризикувати винятком і в більшості випадків не є необхідним.

Пошук / оновлення

Отримуючи довільний елемент масиву, завжди переконайтеся, що він є дійсним індексом щодо довжини масиву:

public Integer getArrayElement(final int index) {
    if (index < 0 || index >= myArray.length) {
        return null; //although I would much prefer an actual exception being thrown when this happens.
    }
    return myArray[index];
}

6

У своєму коді ви отримали доступ до елементів від індексу 0 до довжини рядкового масиву. name.lengthдає кількість рядкових об'єктів у вашому масиві рядкових об'єктів, тобто 3, але ви можете отримати доступ лише до індексу 2 name[2], оскільки до масиву можна отримати доступ від індексу 0 до місця, name.length - 1де ви отримаєте name.lengthкількість об'єктів.

Навіть під час використання forциклу ви почали з нуля індексу, і вам слід закінчити name.length - 1. У масиві a [n] ви можете отримати доступ від форми [0] до a [n-1].

Наприклад:

String[] a={"str1", "str2", "str3" ..., "strn"};

for(int i=0;i<a.length()i++)
    System.out.println(a[i]);

У вашому випадку:

String[] name = {"tom", "dick", "harry"};

for(int i = 0; i<=name.length; i++) {
    System.out.print(name[i] +'\n');
}

5

Для вашого заданого масиву довжина масиву дорівнює 3 (тобто name.length = 3). Але оскільки він зберігає елемент, починаючи з індексу 0, він має максимальний індекс 2.

Отже, замість 'i ** <= name.length' слід написати "i <** name.length", щоб уникнути "ArrayIndexOutOfBoundsException".


3

Стільки для цього простого питання, але я просто хотів виділити нову функцію на Java, яка дозволить уникнути будь-якої плутанини навколо індексації в масивах навіть для початківців. Java-8 абстрагував завдання ітерації для вас.

int[] array = new int[5];

//If you need just the items
Arrays.stream(array).forEach(item -> { println(item); });

//If you need the index as well
IntStream.range(0, array.length).forEach(index -> { println(array[index]); })

Яка користь? Ну, одне - це читабельність, як англійська. По-друге, вам не потрібно турбуватися проArrayIndexOutOfBoundsException


3

Ви отримуєте ArrayIndexOutOfBoundsExceptionчерез i<=name.lengthчастину. name.lengthповерніть довжину рядка name, яка дорівнює 3. Отже, коли ви намагаєтесь отримати доступ name[3], це незаконно і викидає виняток.

Розв’язаний код:

String[] name = {"tom", "dick", "harry"};
for(int i = 0; i < name.length; i++) { //use < insteadof <=
  System.out.print(name[i] +'\n');
}

Це визначено в специфікації мови Java :

public finalПоле length, яке містить число компонентів масиву. lengthможе бути додатним або нульовим.


3

Індекс масиву поза винятком меж

Ось так виглядає цей тип винятку, коли його кидають у Eclipse. Червоне число позначає індекс, до якого ви намагалися отримати доступ. Отже, код виглядатиме так:

myArray[5]

Помилка видається при спробі отримати доступ до індексу, який не існує в цьому масиві. Якщо масив має довжину 3,

int[] intArray = new int[3];

то єдиними дійсними індексами є:

intArray[0]
intArray[1]
intArray[2]

Якщо масив має довжину 1,

int[] intArray = new int[1];

то єдиний дійсний індекс:

intArray[0]

Будь-яке ціле число, яке дорівнює довжині масиву, або більше за нього: знаходиться поза межами.

Будь-яке ціле число менше 0: знаходиться поза межами;

PS: Якщо ви хочете краще зрозуміти масиви і виконувати деякі практичні вправи, тут є відео: підручник з масивів на Java


3

Для багатовимірних масивів може бути важко переконатися, що ви отримуєте доступ до length властивості потрібного виміру. Візьмемо для прикладу наступний код:

int [][][] a  = new int [2][3][4];

for(int i = 0; i < a.length; i++){
    for(int j = 0; j < a[i].length; j++){
        for(int k = 0; k < a[j].length; k++){
            System.out.print(a[i][j][k]);
        }
        System.out.println();
    }
    System.out.println();
}

Кожен вимір має різну довжину, тому тонка помилка полягає в тому, що середня і внутрішня петлі використовують length властивість одного і того ж розміру (тому що a[i].lengthце те саме a[j].length).

Натомість слід використовувати внутрішній цикл a[i][j].length(або a[0][0].length, для простоти).


Додано приклад коду з Чому мій for цикл не працює, коли я змінюю умову (багатовимірні масиви)? оскільки це питання використовується як обдурена ціль для будь-якого питання, що стосується ArrayIndexOutOfBoundsException.
Білл Ящірка

2

Найпоширеніший випадок, який я бачив для, здавалося б, загадкового ArrayIndexOutOfBoundsExceptions, тобто, очевидно, не викликаного вашим власним кодом обробки масиву - це одночасне використання SimpleDateFormat. Зокрема, у сервлеті чи контролері:

public class MyController {
  SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");

  public void handleRequest(ServletRequest req, ServletResponse res) {
    Date date = dateFormat.parse(req.getParameter("date"));
  }
}

Якщо два потоки ввійдуть у метод SimplateDateFormat.parse () разом, ви, ймовірно, побачите ArrayIndexOutOfBoundsException. Зверніть увагу на розділ синхронізації класу javadoc для SimpleDateFormat .

Переконайтесь, що у вашому коді немає місця, яке має доступ до ниткових небезпечних класів, як SimpleDateFormat, паралельно, як у сервлеті чи контролері. Перевірте всі змінні екземпляри сервлетів та контролерів на предмет ймовірних підозрюваних.


2

ArrayIndexOutOfBoundsException всякий раз, коли цей виняток наближається, це означає, що ви намагаєтесь використовувати індекс масиву, який знаходиться поза його межами, або в умовному режимі, ви вимагаєте більше, ніж ви ініціалізували.

Щоб запобігти цьому, завжди переконайтеся, що ви не вимагаєте індексу, якого немає в масиві, тобто якщо довжина масиву дорівнює 10, то ваш індекс повинен бути від 0 до 9


2

ArrayIndexOutOfBounds означає, що ви намагаєтесь індексувати позицію в масиві, який не виділений.

В цьому випадку:

String[] name = { "tom", "dick", "harry" };
for (int i = 0; i <= name.length; i++) {
    System.out.println(name[i]);
}
  • name.length - 3, оскільки масив визначено 3 об'єктами String.
  • При доступі до вмісту масиву позиція починається з 0. Оскільки є 3 елементи, це означатиме ім'я [0] = "tom", name [1] = "dick" та name [2] = "harry
  • Коли ви цикли, оскільки я може бути меншим або рівним name.length, ви намагаєтеся отримати ім'я [3], яке недоступне.

Щоб обійти це ...

  • У своєму циклі для циклу ви можете зробити i <name.length. Це перешкоджатиме циклу на ім'я [3] і замість цього зупинятиметься на імені [2]

    for(int i = 0; i<name.length; i++)

  • Використовуйте для кожного циклу

    String[] name = { "tom", "dick", "harry" }; for(String n : name) { System.out.println(n); }

  • Використовуйте list.forEach (Споживчі дії) (вимагає Java8)

    String[] name = { "tom", "dick", "harry" }; Arrays.asList(name).forEach(System.out::println);

  • Перетворити масив у потік - це хороший варіант, якщо ви хочете виконати додаткові "операції" зі своїм масивом, наприклад, фільтрувати, перетворювати текст, перетворювати на карту тощо (потрібно Java8)

    String[] name = { "tom", "dick", "harry" }; --- Arrays.asList(name).stream().forEach(System.out::println); --- Stream.of(name).forEach(System.out::println);


1

ArrayIndexOutOfBoundsExceptionозначає, що ви намагаєтеся отримати доступ до індексу масиву, який не існує або знаходиться поза межами цього масиву. Індекси масиву починаються від 0 і закінчуються в довжину - 1 .

У вашому випадку

for(int i = 0; i<=name.length; i++) {
    System.out.print(name[i] +'\n'); // i goes from 0 to length, Not correct
}

ArrayIndexOutOfBoundsExceptionтрапляється, коли ви намагаєтесь отримати доступ до індексованого елемента name.length, якого не існує (індекс масиву закінчується на довжину -1). просто заміна <= на <вирішила б цю проблему.

for(int i = 0; i < name.length; i++) {
    System.out.print(name[i] +'\n');  // i goes from 0 to length - 1, Correct
}

1

Для будь-якого масиву довжиною n елементи масиву матимуть індекс від 0 до n-1.

Якщо ваша програма намагається отримати доступ до будь-якого елемента (або пам'яті), що має індекс масиву більше n-1, Java викине ArrayIndexOutOfBoundsException

Ось ось два рішення, які ми можемо використовувати в програмі

  1. Підтримка кількості:

    for(int count = 0; count < array.length; count++) {
        System.out.println(array[count]);
    }

    Або якесь інше циклічне твердження, як

    int count = 0;
    while(count < array.length) {
        System.out.println(array[count]);
        count++;
    }
  2. У цьому способі програмісту не потрібно турбуватися про кількість елементів у масиві.

    for(String str : array) {
        System.out.println(str);
    }

1

Відповідно до вашого Кодексу:

String[] name = {"tom", "dick", "harry"};
for(int i = 0; i<=name.length; i++) {
  System.out.print(name[i] +'\n');
}

Якщо ви перевірите System.out.print (name.length);

ви отримаєте 3;

що означає довжину вашого імені 3

ваш цикл працює від 0 до 3, який повинен працювати або "0 до 2", або "1 до 3"

Відповідь

String[] name = {"tom", "dick", "harry"};
for(int i = 0; i<name.length; i++) {
  System.out.print(name[i] +'\n');
}

1

Кожен елемент масиву називається елементом, і кожен елемент має доступ за його числовим індексом. Як показано на попередній ілюстрації, нумерація починається з 0 . Наприклад, до 9-го елемента можна отримати доступ до індексу 8.

IndexOutOfBoundsException кидається, щоб вказати, що якийсь індекс (наприклад, масив, рядок або вектор) знаходиться поза діапазоном.

До будь-якого масиву X можна отримати доступ від [0 до (довжина X. - 1)]


1

Я бачу, що всі відповіді тут пояснюють, як працювати з масивами та як уникнути індексу поза винятками. Я особисто уникаю масивів за будь-яку ціну. Я використовую класи Collections, що дозволяє уникнути усієї дурості, коли не доведеться повністю мати справу з індексами масиву. Циклічні конструкції прекрасно працюють з колекціями, що підтримують код, який простіше писати, розуміти та підтримувати.


1

Якщо ви використовуєте довжину масиву для управління ітерацією циклу for , завжди пам’ятайте, що індекс першого елемента в масиві дорівнює 0 . Отже індекс останнього елемента в масиві на один менший, ніж довжина масиву.


0

ArrayIndexOutOfBoundsException саме ім'я пояснює, що якщо ви намагаєтеся отримати доступ до значення в індексі, який не входить до сфери розміру масиву, виникають такі винятки.

У вашому випадку ви можете просто видалити знак рівності зі свого циклу.

for(int i = 0; i<name.length; i++)

Кращий варіант - це повторити масив:

for(String i : name )
      System.out.println(i);

0

Ця помилка виникає під час перемикання циклу запуску. Розглянемо простий приклад, як цей,

class demo{
  public static void main(String a[]){

    int[] numberArray={4,8,2,3,89,5};

    int i;

    for(i=0;i<numberArray.length;i++){
        System.out.print(numberArray[i+1]+"  ");
    }
}

Спочатку я ініціалізував масив як "numberArray". потім деякі елементи масиву друкуються за допомогою циклу. Коли цикл працює 'i' time, надрукуйте елемент (numberArray [i + 1]) .. (коли значення i дорівнює 1, друкується елемент numberArray [i + 1].) Припустимо, що при i = (числоArray. length-2), друкується останній елемент масиву. Коли значення 'i' переходить до (numberArray.length-1), значення для друку немає. У цьому пункті виникає 'ArrayIndexOutOfBoundsException'. Я сподіваюся, що ви зможете отримати idea.thank you!


0

Ви можете використовувати Факультативно у функціональному стилі, щоб уникнути NullPointerExceptionта ArrayIndexOutOfBoundsException:

String[] array = new String[]{"aaa", null, "ccc"};
for (int i = 0; i < 4; i++) {
    String result = Optional.ofNullable(array.length > i ? array[i] : null)
            .map(x -> x.toUpperCase()) //some operation here
            .orElse("NO_DATA");
    System.out.println(result);
}

Вихід:

AAA
NO_DATA
CCC
NO_DATA

-4

Ви не змогли повторити чи зберегти більше даних, ніж довжина масиву. У цьому випадку ви можете зробити так:

for (int i = 0; i <= name.length - 1; i++) {
    // ....
}

Або це:

for (int i = 0; i < name.length; i++) {
    // ...
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.