Разница между неявными и явными объявлениями размера ArrayList?

в чем разница между следующие объявления:

List list1 = new ArrayList();

List list2 = new ArrayList(10);

по умолчанию выделяет его с 10. Но есть ли разница? Могу ли я добавить 11-й элемент в list2 by list2.add("something")?

6 ответов


здесь исходный код для вас для первого примера

public  ArrayList() {
    this(10);
 }

так что нет никакой разницы. Так как начальная емкость 10, независимо от того, передаете вы 10 или нет, он инициализируется емкостью 10.

могу ли я добавить 11-й элемент в list2 по list2.добавить ("что-то")?

конечно, начальная емкость не является конечной емкостью. Так как вы продолжаете добавлять более 10, размер списка сохраняется возрастающий.

если вы хотите иметь контейнер фиксированного размера, используйте Array или java.util.Collections.unmodifiableList()

стоит прочитать об этом изменении в Java 8:в Java 8, Почему емкость по умолчанию ArrayList теперь равна нулю?

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


вы всегда можете добавить элементы в список. Однако массив inlying, который используется ArrayList, инициализируется либо размером по умолчанию 10, либо размером, который вы указываете при инициализации ArrayList. Это означает, что если вы, например, добавляете 11-й элемент, размер массива должен быть увеличен, что делается путем копирования содержимого массива в новый экземпляр массива большего размера. Это, конечно, требует времени в зависимости от размера списка/массива. Так что если вы уже знаете, что ваш список будет содержать тысячи элементов, это быстрее, если вы уже инициализируете список с таким приблизительным размером.


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

указание этой начальной емкости часто является преждевременной оптимизацией, но если вам действительно нужно ArrayList из 10 элементов вы должны указать его явно, а не предполагать что размер по умолчанию-10. Хотя это действительно было поведение по умолчанию (до JDK 7, IIRC), вы не должны полагаться на него - JDK 8 (проверено с java-1.8.0-openjdk-1.8.0.101-1.b14.fc24.x86_64 Я установил) создает пустой ArrayLists по умолчанию.


другие ответы объяснили очень хорошо, но просто, чтобы сохранить вещи актуальными, в JDK 1.7.0_95:

/**
 * Constructs a new {@code ArrayList} instance with zero initial capacity.
 */
public ArrayList() {
    array = EmptyArray.OBJECT;
}


/**
 * Constructs a new instance of {@code ArrayList} with the specified
 * initial capacity.
 *
 * @param capacity
 *            the initial capacity of this {@code ArrayList}.
 */
public ArrayList(int capacity) {
    if (capacity < 0) {
        throw new IllegalArgumentException("capacity < 0: " + capacity);
    }
    array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
}

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

и еще интереснее здесь переменная (с комментарием), которая сама по себе дает много информации:

/**
 * The minimum amount by which the capacity of an ArrayList will increase.
 * This tuning parameter controls a time-space tradeoff. This value (12)
 * gives empirically good results and is arguably consistent with the
 * RI's specified default initial capacity of 10: instead of 10, we start
 * with 0 (sans allocation) and jump to 12.
 */
private static final int MIN_CAPACITY_INCREMENT = 12;

вы только что выбрали идеальный пример. Оба на самом деле делают то же самое, что new ArrayList() звонки this(10) ;) но внутренне он определил бы массив удержания с размером 10. the ArrayList#size метод с другой стороны просто возвращает переменную size, который будет изменен только после добавления и удаления элементов. Эта переменная также является основной причиной для IOOB исключения. Так что вы не сможете этого сделать.

если вы проверяете код ArrayList например, youll обратите внимание, что метод ArrayList#add будем называть ArrayList#rangeCheck. Проверка диапазона на самом деле просто заботится о size переменная, а не фактическая длина массива, содержащего данные для List.

из-за этого вы все равно не сможете вставить данные по индексу 5 например. Внутренняя длина массива данных в этот момент будет 10, но так как вы ничего не добавляете в свой List на size переменная еще быть 0 и вы получите правильный IndexOutOfBoundsException когда вы попытаетесь это сделать.

просто попробуйте позвонить list.size() после инициализации List С любым размером, и вы заметите, что возвращенный размер будет 0.


инициализация ArrayList была оптимизирована с JDK 1.7 update 40, и есть хорошее объяснение о двух разных поведениях по этой ссылке java-оптимизация-пустой-arraylist-и-Hashmap-стоимость-меньше-памяти-jdk-17040-update.

Итак, перед Java 1.7u40 нет никакой разницы, но от этой версии есть довольно существенная разница.

эта разница касается оптимизации производительности и не изменяет контракт List.add(E e) и ArrayList(int initialCapacity).