Зачем начинать список ArrayList с начальной емкости?

Обычным конструктором ArrayList является:

ArrayList<?> list = new ArrayList<>();

Но есть также перегруженный конструктор с параметром для его начальной емкости:

ArrayList<?> list = new ArrayList<>(20);

Почему полезно создавать ArrayList с начальной емкостью, если мы можем добавлять к нему все, что захотим?

Комментарии к вопросу (4)
Решение

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

Чем больше конечный список, тем больше времени вы экономите, избегая перераспределения.

Тем не менее, даже без предварительного распределения, вставка n элементов в конец ArrayList гарантированно займет время O(n). Другими словами, добавление элемента - это амортизированная операция постоянного времени. Это достигается тем, что каждое перераспределение увеличивает размер массива экспоненциально, обычно в 1.5 раза. При таком подходе общее количество операций может быть показано как O(n).

Комментарии (11)

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

Поэтому, если вы знаете, что ваш верхний предел составляет 20 пунктов, то при создании массива с начальной длиной 20 лучше, чем по умолчанию, скажем, 15 и затем изменить ее размер на 15*2 = 30` и использовать только 20, а тратить циклы на расширение.

С. П. - Как AmitG говорит, коэффициент расширения является определенной реализацией (в данном случае (oldCapacity * 3)/2 + 1)

Комментарии (2)

По умолчанию размер списка массивов равен 10.

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

Поэтому если вы собираетесь добавить 100 или более записей, вы можете увидеть накладные расходы на перераспределение памяти.

ArrayList<?> list = new ArrayList();    
// same as  new ArrayList(10);      

Поэтому если у вас есть представление о количестве элементов, которые будут храниться в Arraylist, лучше создать Arraylist с этим размером, а не начинать с 10 и затем увеличивать его.

Комментарии (1)

Я написал блог по теме 2 месяца назад. Статья для C#и#39;список З в <Т> Но Java'ы коллекции имеет очень похожую реализацию. Поскольку коллекции реализуется с помощью динамического массива, он увеличивается в размерах по требованию. Так что причин для конструктора потенциал для оптимизации.

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

Пример

Вот пример, как коллекции будет увеличиваться в размерах:

10
16
25
38
58
... 17 resizes ...
198578
297868
446803
670205
1005308

Так что список начинается с емкостью 10, Когда 11-й элемент добавляется это увеличение на 50% + 1на16. На 17-й пункт вколлекцииснова увеличивается до25и так далее. Теперь рассмотрим пример, где мы&#39;вновь создать список, в котором нужные способности уже известны как1000000. Созданиеколлекциибез конструктора размер будет называть класса ArrayList.добавить 1000000 времена, которые берет О(1) обычно, либо о(н) на размер.

1000000 + 16 + 25 + ... + 670205 + 1005308 = 4015851 операции

Сравните это с помощью конструктора, а затем звонит в коллекцию ArrayList.добавить`, который гарантированно работает в О(1).

1000000 + 1000000 = операции 2000000

На Java против c#

Java-это, как указано выше, начиная с 10 и растет каждый размер на 50% + 1. В C# начинается на4и увеличивается гораздо более агрессивно, удваивая на каждом размер. В1000000добавляет примере выше для операций c# использует3097084`.

Ссылки

Комментарии (0)

Установка начального размера списка ArrayList, например, ArrayList(100), уменьшает количество повторных выделений внутренней памяти.

Пример:

ArrayList example = new ArrayList(3);
example.add(1); // size() == 1
example.add(2); // size() == 2, 
example.add(2); // size() == 3, example has been 'filled'
example.add(3); // size() == 4, example has been 'expanded' so that the fourth element can be added. 

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

"Каждый экземпляр ArrayList имеет емкость. Емкость - это размер массива, используемого для хранения элементов списка. Она всегда по крайней мере, больше размера списка. По мере добавления элементов в ArrayList, его емкость автоматически увеличивается. Детали роста политики не определены, кроме того, что добавление элемента имеет постоянную амортизированную стоимость времени"

Очевидно, что если у вас нет представления о том, какой диапазон вы будете хранить, установка размера, вероятно, не будет хорошей идеей - однако, если у вас есть конкретный диапазон, установка начальной емкости увеличит эффективность использования памяти.

Комментарии (0)

ArrayList может содержать много значений, и когда делаешь большие первоначальные вставок вы можете сказать ArrayList для выделить большой объем памяти для начала, чтобы не тратить циклы процессора, когда он пытается выделить больше места для следующего элемента. Таким образом, чтобы выделить определенное пространство в начале более эффективной.

Комментарии (0)

Это позволит избежать возможные усилия для перераспределения для каждого объекта.

int newCapacity = (oldCapacity * 3)/2 + 1;

создан новый объект внутри [] - это.<БР> JVM необходима усилия, чтобы создать новый объект[]при добавлении элемента в коллекцию ArrayList. Если вы Don&#39;т иметь выше код(любой алгоритм вы думаете) для перераспределения затем каждый раз, когда вы вызываетекласса ArrayList.добавить ()", затем " новый объект[]должен быть создан, который бессмысленно и мы теряем время для увеличения размера по 1 для каждого объекты, которые будут добавлены. Так что лучше, чтобы увеличить размер объекта[] с помощью следующей формулы.<БР> (С JSL использовал формулу прогнозирования, приведенные ниже, динамично растущей коллекции, а не увеличивается на 1 каждый раз. Потому что расти он принимает усилия для JVM)

int newCapacity = (oldCapacity * 3)/2 + 1;
Комментарии (4)

Я'd для его оптимизации. Класса ArrayList без первоначального емкость будет ~10 пустых строк и будет расширяться, когда вы делаете добавить.

Иметь список с точно число элементов, вам нужно позвонить метода trimtosize()

Комментарии (0)

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

Комментарии (0)

Как на мой опыт с коллекции, давая начальная емкость является хорошим способом, чтобы избежать расходов на перераспределение. Но он несет в себе предостережение. Все предложения, упомянутые выше, говорят, что нужно предоставить первоначального потенциала только тогда, когда грубая оценка числа элементов известно. Но когда мы пытаемся предоставить первичную емкость без какой-либо идеи, объем памяти, зарезервированных и неиспользуемых будет отходов, как он может никогда не потребоваться после того, как список заполняется необходимое количество элементов. Я хочу сказать, мы можем быть прагматиками в начале при выделении мощности, а затем найти умный способ познания требуется минимальная мощность во время выполнения. Класса ArrayList содержит метод, называемый ensureCapacity(тип int minCapacity). Но тогда, стоит найти умный способ...

Комментарии (0)

Я испытал ArrayList с С и без initialCapacity и меня удивляют результатом <БР/> Когда я LOOP_NUMBER до 100 000 или меньше, в итоге получается, что установка initialCapacity является эффективным. <БР>

list1Sttop-list1Start = 14
list2Sttop-list2Start = 10

<БР/> Но когда я установил LOOP_NUMBER до 1000000 результатом изменения: <БР/>

list1Stop-list1Start = 40
list2Stop-list2Start = 66

<БР/> Наконец, я не мог'т выяснить, как это работает?! <БР/> Пример кода:

 public static final int LOOP_NUMBER = 100000;

public static void main(String[] args) {

    long list1Start = System.currentTimeMillis();
    List list1 = new ArrayList();
    for (int i = 0; i < LOOP_NUMBER; i++) {
        list1.add(i);
    }
    long list1Stop = System.currentTimeMillis();
    System.out.println("list1Stop-list1Start = " + String.valueOf(list1Stop - list1Start));

    long list2Start = System.currentTimeMillis();
    List list2 = new ArrayList(LOOP_NUMBER);
    for (int i = 0; i < LOOP_NUMBER; i++) {
        list2.add(i);
    }
    long list2Stop = System.currentTimeMillis();
    System.out.println("list2Stop-list2Start = " + String.valueOf(list2Stop - list2Start));
}

Я испытал на Windows8.1 и jdk1.7.0_80

Комментарии (1)