Обычным конструктором ArrayList
является:
ArrayList<?> list = new ArrayList<>();
Но есть также перегруженный конструктор с параметром для его начальной емкости:
ArrayList<?> list = new ArrayList<>(20);
Почему полезно создавать ArrayList
с начальной емкостью, если мы можем добавлять к нему все, что захотим?
Если вы заранее знаете, какого размера будет ArrayList
, эффективнее указать начальную емкость. Если этого не сделать, внутренний массив придется многократно перераспределять по мере роста списка.
Чем больше конечный список, тем больше времени вы экономите, избегая перераспределения.
Тем не менее, даже без предварительного распределения, вставка n
элементов в конец ArrayList
гарантированно займет время O(n)
. Другими словами, добавление элемента - это амортизированная операция постоянного времени. Это достигается тем, что каждое перераспределение увеличивает размер массива экспоненциально, обычно в 1.5
раза. При таком подходе общее количество операций может быть показано как O(n)
.
Потому что коллекции
является динамическое изменение размера массива структура данных, которая означает, что она реализуется как массив с исходным (по умолчанию) фиксированного размера. При этом заполняется массив будет продлен до двойного размера. Эта операция является дорогостоящей, поэтому вы хотите как можно меньше.
Поэтому, если вы знаете, что ваш верхний предел составляет 20 пунктов, то при создании массива с начальной длиной 20 лучше, чем по умолчанию, скажем, 15 и затем изменить ее размер на 15*2 = 30` и использовать только 20, а тратить циклы на расширение.
С. П. - Как AmitG говорит, коэффициент расширения является определенной реализацией (в данном случае (oldCapacity * 3)/2 + 1
)
По умолчанию размер списка массивов равен 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 и затем увеличивать его.
Я написал блог по теме 2 месяца назад. Статья для C#и#39;список З в <Т>
Но Java'ы коллекции
имеет очень похожую реализацию. Поскольку коллекции
реализуется с помощью динамического массива, он увеличивается в размерах по требованию. Так что причин для конструктора потенциал для оптимизации.
Когда один из этих операции resizings возникает, коллекции копирует содержимое массива в новый массив, который является вдвое большей емкостью, чем старый. Эта операция выполняется в О(Н) время.
Вот пример, как коллекции
будет увеличиваться в размерах:
10
16
25
38
58
... 17 resizes ...
198578
297868
446803
670205
1005308
Так что список начинается с емкостью 10
, Когда 11-й элемент добавляется это увеличение на 50% + 1на
16. На 17-й пункт в
коллекцииснова увеличивается до
25и так далее. Теперь рассмотрим пример, где мы'вновь создать список, в котором нужные способности уже известны как
1000000. Создание
коллекциибез конструктора размер будет называть класса ArrayList.добавить
1000000
времена, которые берет О(1) обычно, либо о(н) на размер.
1000000 + 16 + 25 + ... + 670205 + 1005308 = 4015851 операции
Сравните это с помощью конструктора, а затем звонит в коллекцию ArrayList.добавить`, который гарантированно работает в О(1).
1000000 + 1000000 = операции 2000000
Java-это, как указано выше, начиная с 10
и растет каждый размер на 50% + 1. В C# начинается на
4и увеличивается гораздо более агрессивно, удваивая на каждом размер. В
1000000добавляет примере выше для операций c# использует
3097084`.
Установка начального размера списка ArrayList, например, ArrayList<>(100)
, уменьшает количество повторных выделений внутренней памяти.
Пример:
ArrayList example = new ArrayList<Integer>(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, его емкость автоматически увеличивается. Детали роста политики не определены, кроме того, что добавление элемента имеет постоянную амортизированную стоимость времени"
Очевидно, что если у вас нет представления о том, какой диапазон вы будете хранить, установка размера, вероятно, не будет хорошей идеей - однако, если у вас есть конкретный диапазон, установка начальной емкости увеличит эффективность использования памяти.
ArrayList может содержать много значений, и когда делаешь большие первоначальные вставок вы можете сказать ArrayList для выделить большой объем памяти для начала, чтобы не тратить циклы процессора, когда он пытается выделить больше места для следующего элемента. Таким образом, чтобы выделить определенное пространство в начале более эффективной.
Это позволит избежать возможные усилия для перераспределения для каждого объекта.
int newCapacity = (oldCapacity * 3)/2 + 1;
создан новый объект внутри []
- это.<БР> JVM необходима усилия, чтобы создать новый объект[]при добавлении элемента в коллекцию ArrayList. Если вы Don'т иметь выше код(любой алгоритм вы думаете) для перераспределения затем каждый раз, когда вы вызываете
класса ArrayList.добавить ()", затем " новый объект[]должен быть создан, который бессмысленно и мы теряем время для увеличения размера по 1 для каждого объекты, которые будут добавлены. Так что лучше, чтобы увеличить размер объекта[]
с помощью следующей формулы.<БР>
(С JSL использовал формулу прогнозирования, приведенные ниже, динамично растущей коллекции, а не увеличивается на 1 каждый раз. Потому что расти он принимает усилия для JVM)
int newCapacity = (oldCapacity * 3)/2 + 1;
Я'd для его оптимизации. Класса ArrayList без первоначального емкость будет ~10 пустых строк и будет расширяться, когда вы делаете добавить.
Иметь список с точно число элементов, вам нужно позвонить метода trimtosize()
Я думаю, что в каждой коллекции создается со значением емкости инициализации на "10 и". Так что в любом случае, если вы создаете объект ArrayList без создания потенциала в конструктор, то он будет создан со значением по умолчанию.
Как на мой опыт с коллекции
, давая начальная емкость является хорошим способом, чтобы избежать расходов на перераспределение. Но он несет в себе предостережение. Все предложения, упомянутые выше, говорят, что нужно предоставить первоначального потенциала только тогда, когда грубая оценка числа элементов известно. Но когда мы пытаемся предоставить первичную емкость без какой-либо идеи, объем памяти, зарезервированных и неиспользуемых будет отходов, как он может никогда не потребоваться после того, как список заполняется необходимое количество элементов. Я хочу сказать, мы можем быть прагматиками в начале при выделении мощности, а затем найти умный способ познания требуется минимальная мощность во время выполнения. Класса ArrayList содержит метод, называемый ensureCapacity(тип int minCapacity)
. Но тогда, стоит найти умный способ...
Я испытал 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<Integer> 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<Integer> 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