Ошибка "Index -1 out of bounds for length 2"?

Подскажите, пожалуйста, почему компилятор выдает ошибку:

"Index -1 out of bounds for length 2"

Как ее исправить?

Вот код:

public void merge(int[] nums1, int m, int[] nums2, int n) {
    if (m == 0) {
        System.out.println("Обратите внимание, что поскольку m = 0, в nums1 нет элементов. 0 присутствует только для того, чтобы результат слияния мог поместиться в nums1.");
    }
    ArrayList<Integer> w1 = new ArrayList<>();
    ArrayList<Integer> w2 = new ArrayList<>();
    if (nums2.length == 0) {
        System.out.println(Arrays.toString(new String[]{Arrays.toString(nums1)}));
    } else {
        for (int el : nums1) {
            w1.add(el);
        }
        int r = nums1.length;
    
        for (int i = m; w1.size() != m; i++) {
            w1.removeLast();
        }
    
        for (int el : nums2) {
            w2.add(el);
        }
        int z = nums1.length;
        for (int i = n; w2.size() != n; i--) {
            w2.remove(i - 1);
        }
        Collections.sort(w1);
        Collections.sort(w2);
        System.out.println(w1.toString());
        System.out.println(w2.toString());
    }
}

Ответы (2 шт):

Автор решения: Nowhere Man

Компилятор не может выдавать указанную ошибку, так как она возникает при выполнении программы.

Судя по коду, она возникает в строке w2.remove(i - 1); при удалении некоторых элементов из списка w2, так как переменная цикла i быстро уменьшается, а условие i > 0 в цикле не проверяется.

Другие проблемы в представленном коде:

  • переменные r, z проинициализированы одним и тем же значением, но нигде не используются.
  • переменные m, n не сопоставляются с размерами соответствующих массивов nums1, nums2 в том смысле, что они могут превышать размеры массивов.
  • в списки w1, w2 зачем-то полностью копируется содержимое массивов nums1, nums2, а затем начинаются циклы для удаления элементов из конца списка w1 и начала списка w2 -- значительно проще было бы сразу скопировать нужное число элементов
  • несмотря на название метода merge и текст сообщения о неком слиянии, данный метод в лучшем случае распечатает содержимое двух отсортированных списков w1 и w2.

Вариант исправленного кода может выглядеть так:

public void merge(int[] nums1, int m, int[] nums2, int n) {
    if (m == 0) {
        System.out.println("Обратите внимание, что поскольку m = 0, в nums1 нет элементов. 0 присутствует только для того, чтобы результат слияния мог поместиться в nums1.");
    }
    if (nums2.length == 0) {
        System.out.println(Arrays.toString(nums1));
        return;
    }
    // создадим списки с нужной ёмкостью
    List<Integer> w1 = new ArrayList<>(m);
    List<Integer> w2 = new ArrayList<>(n);

    // копируем не больше первых m элементов nums1 в список w1
    for (int i = 0, r = Math.min(nums1.length, m); i < r; i++) {
        w1.add(nums1[i]);
    }

    // копируем не больше последних n элементов nums2 в список w2
    for (int z = nums2.length, i = Math.max(z - n, 0); i < z; i++) {
        w2.add(nums2[i]);
    }

    Collections.sort(w1);
    Collections.sort(w2);
    // метод toString() вызывается по умолчанию при выводе каждого списка
    System.out.println(w1); 
    System.out.println(w2);
}

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

Пример реализации:

public void merge(int[] nums1, int m, int[] nums2, int n) {
    if (m == 0) {
        System.out.println("Обратите внимание, что поскольку m = 0, в nums1 нет элементов. 0 присутствует только для того, чтобы результат слияния мог поместиться в nums1.");
    }
    if (nums2.length == 0) {
        System.out.println(Arrays.toString(nums1));
        return;
    }
    // создадим нужные списки
    var w1 = Arrays.stream(nums1)
                   .limit(m)
                   .sorted().boxed().toList();
    var w2 = Arrays.stream(nums2)
                   .skip(Math.max(nums2.length - n, 0))
                   .sorted().boxed().toList();

    System.out.println(w1);
    System.out.println(w2);
}   
→ Ссылка
Автор решения: DavidJava

Эта ошибка происходит при выходе индекса из границ массива. То есть, если вы создаёте массив с размером 5, то индексы у этого массива от 0 до 4. Если вы укажете индекс отрицательный или число больше 4 то выскочит эта ошибка. Она определяется классом ArrayIndexOutOfBoundsException. Её можно поймать в блоке try-catch.

→ Ссылка