Подтвердить что ты не робот

Начальная емкость ArrayList и IndexOutOfBoundsException

Рассмотрим этот пример кода:

List<String> myList = new ArrayList<String>(7);
myList.add(5, "Hello");
myList.removeAll(Collections.singleton(null));

System.out.println(myList.size() + " objects:" );
for (String s : myList) {
    System.out.println("\t" + s);
}

myList инициализируется начальной емкостью 7, затем следующая строка пытается добавить строку "Hello" в позиции 5. Это вызывает исключение IndexOutOfBoundsException:

Исключение в потоке "main" java.lang.IndexOutOfBoundsException: Index: 5, Size: 0

Я просмотрел этот вопрос о том, что означает "начальная емкость" в терминах ArrayList. Я понимаю, что этот конкретный конструктор выделяет место для 7 элементов String, и если мы попытаемся добавить в список 8 элементов, ему придется выделить больше места.

Я не понимаю, почему он не создает "пустой" список размером 7, с нулевыми значениями в каждом индексе, похожим на то, что произойдет, если мы объявим String[] myArray = new String[7]. Я помню, как узнал, что ArrayList представляет собой реализацию Java динамического массива, поэтому я бы ожидал подобного рода поведения. Если у меня на самом деле нет места для 7 строк, выделенных при объявлении new ArrayList<String>(7), что на самом деле происходит?

4b9b3361

Ответ 1

Я не понимаю, почему он не создает "пустой" список размером 7, с нулевыми значениями в каждом индексе, похожим на то, что произойдет, если мы объявим String [] myArray = new String [7].

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

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

Нет, это действительно не так. Это список, который можно изменить и использовать массив за кулисами. Старайтесь не думать об этом как о массиве.

Если у меня на самом деле нет места для 7 строк, выделенных при объявлении нового ArrayList<String>(7), что на самом деле происходит?

У вас есть место для 7 ссылок на строки. Размер буфера (т.е. Емкость) не менее 7, но логический размер списка по-прежнему равен 0 - вы ничего не добавили к нему. Это похоже на то, что у вас есть листок бумаги, который достаточно длинный для 7 строк, но вы еще ничего не писали.

Если вам нужен предварительно заполненный список, вы можете легко написать способ его создания:

public static List<T> createPrefilledList(int size, T item) {
    ArrayList<T> list = new ArrayList<T>(size);
    for (int i = 0; i < size; i++) {
        list.add(item);
    }
}

Ответ 2

Существует разница между исходной емкостью массива и ее размером (т.е. количеством элементов, содержащихся в вашем массиве). Это его размер, который используется для определения того, пытаетесь ли вы получить доступ к индексу, который выходит за рамки.

Вот метод ArrayList.java, который выполняет эту проверку:

 private void rangeCheckForAdd(int index) {
   if (index < 0 || index > this.size)
     throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
 }

Как вы можете видеть, это не имеет никакого отношения к начальной емкости массива. Он основан исключительно на количестве содержащихся в нем элементов.

Ответ 3

Начальная емкость делает только одно: она дает предложение (а не требование) о том, насколько большой должен быть массив поддержки. Логически, нет никакой разницы между тем, какие операции разрешены с предложением или без него, или что такое предложение. Единственные изменения будут в том, какие внутренние операции могут произойти или не произойти в качестве предложения оптимизации.

Вы можете только "добавить" к этим позициям, которые уже существуют в массиве. Элементы перед положением 5 еще не существуют, поэтому он выдает исключение. Из Javadoc:

Броски: IndexOutOfBoundsException - если индекс вне диапазона (index < 0 || index > size())