it-swarm.com.ru

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

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

Удивительно, но комментарий к конструктору по умолчанию (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;
}
80
kevinarpe

Технически, это 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 гарантируется lazily, оптимизация, присутствующая также в Java 7.

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

Фон

Вот электронная почта от Майка Дуйгоу

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

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

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

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

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

97
Lukas Eder

В 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

Теперь вопрос, почему это изменение было сделано в Java 8? 

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

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

Статья Емкость ArrayList по умолчанию в Java 8 объясняет это в деталях.

11
Shailesh Vikram Singh

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

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

6
supercat

Вопрос «почему?».

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

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

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

3
ya_pulser

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

 Please see below

0
Rahul Maurya

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

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

Снимок экрана ссылка.

0
tech_lover