В Java 8, почему емкость по умолчанию ArrayList равна нулю?

Насколько я помню, до Java 8 емкость по умолчанию ArrayList составляла 10.

Удивительно, что комментарий конструктора default (void) все еще говорит: Constructs an empty list with an initial capacity of ten.

От ArrayList.java:

/**
 * Shared empty array instance used for default sized empty instances. We
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
 * first element is added.
 */
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

...

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

Ответ 1

Технически, это 10, а не ноль, если вы допускаете ленивую инициализацию массива поддержки. См:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

где

/**
 * Default initial capacity.
 */
private static final int DEFAULT_CAPACITY = 10;

То, о чем вы говорите, - это только начальный объект массива нулевого размера, который распределяется между всеми первоначально пустыми объектами ArrayList. То есть производительность 10 гарантируется лениво, оптимизация, которая присутствует также в Java 7.

По общему признанию, контракт конструктора не совсем точен. Возможно, это источник путаницы здесь.

Фон

Здесь электронная почта Майка Дуйгу

Я опубликовал обновленную версию пустого патча ArrayList и HashMap.

http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/

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

Для HashMap используется использование поля порогового значения для отслеживания начального размера запроса до тех пор, пока не понадобится массив ведра. На стороне чтения пустой случай карты проверяется с помощью isEmpty(). При размере записи сравнение (таблица == EMPTY_TABLE) используется для обнаружения необходимости раздувания массива ведра. В readObject есть немного больше работы, чтобы попытаться выбрать эффективную начальную емкость.

От: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html

Ответ 2

Если первая операция, выполняемая с помощью ArrayList, должна передать addAll коллекцию, содержащую более десяти элементов, то любое усилие, созданное начальным десятиэлементным массивом для хранения содержимого ArrayList, будет выброшено окно. Всякий раз, когда что-то добавляется в ArrayList, необходимо проверить, будет ли размер результирующего списка превышать размер хранилища резервных копий; позволяя первоначальному хранилищу поддержки иметь нулевой размер, а не десять, приведет к тому, что этот тест завершится с лишним лишним раз в течение жизни списка, первая операция которого - это "добавить", для чего потребуется создать начальный массив из десяти элементов, но эта стоимость меньше стоимости создания массива из десяти элементов, который никогда не становится привычным.

Было сказано, что в некоторых контекстах было бы возможно улучшить производительность, если была перегрузка "addAll", которая указала, сколько элементов (если они есть), вероятно, будут добавлены в список после настоящего, и который может использовать это, чтобы влиять на его поведение распределения. В некоторых случаях код, который добавляет последние несколько элементов в список, будет иметь неплохую идею о том, что списку никогда не понадобится какое-то пространство за его пределами. Существует много ситуаций, когда список будет заполняться один раз и никогда не будет изменен после этого. Если в точечном коде известно, что максимальный размер списка будет составлять 170 элементов, он имеет 150 элементов и резервное хранилище размером 160, а рост резервного хранилища до размера 320 будет бесполезным и оставит его на уровне 320 или обрезает его до 170 будет менее эффективным, чем просто следующее распределение вырастет до 170.

Ответ 3

Вопрос: "почему?".

Проверки профилирования памяти (например (https://www.yourkit.com/docs/java/help/inspections_mem.jsp#sparse_arrays) показывает, что пустые (заполненные нулями) массивы занимают тонны памяти.

Размер по умолчанию 10 объектов означает, что мы выделяем 10 указателей (40 или 80 байт) для базового массива при создании и заполняем их нулями. Реальные Java-приложения создают миллионы списков массивов.

Введенная модификация удаляет ^ W, откладывает это потребление памяти до момента, когда вы действительно будете использовать список массивов.

Ответ 4

В java 8 по умолчанию массив ArrayList равен 0, пока мы не добавим хотя бы один объект в объект ArrayList. Пожалуйста, см. Ниже код для справки.

ArrayList al = new ArrayList();          //Size:  0, Capacity:  0
ArrayList al = new ArrayList(5);         //Size:  0, Capacity:  5
ArrayList al = new ArrayList(new ArrayList(5)); //Size:  0, Capacity:  0
al.add( "shailesh" );                    //Size:  1, Capacity: 10



public static void main( String[] args )
        throws Exception
    {
        ArrayList al = new ArrayList();
        getCapacity( al );
        al.add( "shailesh" );
        getCapacity( al );
    }

    static void getCapacity( ArrayList<?> l )
        throws Exception
    {
        Field dataField = ArrayList.class.getDeclaredField( "elementData" );
        dataField.setAccessible( true );
        System.out.format( "Size: %2d, Capacity: %2d%n", l.size(), ( (Object[]) dataField.get( l ) ).length );
}

Response: - 
Size:  0, Capacity:  0
Size:  1, Capacity: 10

Ответ 5

После вышеупомянутого вопроса я прошел через ArrayList Document of Java 8. Я обнаружил, что размер по умолчанию все еще равен 10.

См. ниже

Ответ 6

Размер ArrayList по умолчанию в JAVA 8 равен 10. Единственное изменение, внесенное в JAVA 8, заключается в том, что если кодер добавляет элементы меньше 10, то оставшиеся пустые места arraylist не задаются нулями. Говоря так, потому что я сам пережил эту ситуацию, и затмение заставило меня заглянуть в это изменение JAVA 8.

Вы можете обосновать это изменение, посмотрев снимок экрана. В нем вы можете видеть, что размер ArrayList указан как 10 в Object [10], но количество отображаемых элементов - это всего 7. Остальные элементы значения null здесь не отображаются. В JAVA 7 ниже снимок экрана аналогичен только одному изменению, в котором также отображаются элементы нулевого значения, для которых кодеру необходимо написать код для обработки нулевых значений, если он выполняет итерацию полного списка массивов, в то время как в JAVA 8 это бремя удаляется из глава кодера/разработчика.

Снимок экрана.