Подтвердить что ты не робот

Почему векторный массив удваивается?

Почему классическая реализация Vector (ArrayList для Java-людей) удваивает размер внутреннего массива на каждом расширении вместо того, чтобы утроить или увеличивать в четыре раза?

4b9b3361

Ответ 1

При вычислении среднего времени для вставки в вектор вам нужно разрешить не растущие вставки и растущие вставки.

Вызов общего количества операций для вставки n элементов o total, а средний o среднийк югу > .

Если вы вставляете элементы n, и, если требуется, вы увеличиваетесь в A, тогда есть o total= n + & Sigma; A i [0 < я < 1 + ln A n]. В худшем случае вы используете 1/A выделенного хранилища.

Интуитивно, A = 2 означает, что в худшем случае вы o total= 2n, поэтому o average - O (1), а в худшем случае вы используете 50% выделенного хранилища.

Для более крупного A у вас есть более низкий o total, но больше теряется в памяти.

Для меньшего A, o total больше, но вы не тратите столько на хранение. Пока он растет геометрически, он все еще O (1) амортизирует время вставки, но константа будет выше.

Для факторов роста 1,25 (красный), 1,5 (голубой), 2 (черный), 3 (синий) и 4 (зеленый), эти графики показывают эффективность точки и среднего размера (отношение размера/выделенного пространства, ) слева и эффективность времени (соотношение входов/операций, лучше - справа) справа для вставки 400 000 предметов. 100% -ная эффективность пространства достигается для всех факторов роста непосредственно перед изменением размера; случай для A = 2 показывает эффективность времени между 25% и 50% и экономию пространства около 50%, что хорошо для большинства случаев:

space and time efficiency graph - C like implementations

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

space and time efficiency graph - Java like implementations

Ответ 2

Экспоненциальное удвоение размера массива (или строки) является хорошим компромиссом между наличием достаточного количества ячеек в массиве и слишком большим объемом памяти.

Скажем, мы начинаем с 10 элементов:

1 - 10
2 - 20
3 - 40
4 - 80
5 - 160

Когда мы утроим размер, мы становимся слишком быстрыми

1 - 10
2 - 30
3 - 90
4 - 270
5 - 810

На практике вы будете расти, возможно, 10 или 12 раз. Если вы утроите, вы, возможно, сделаете это 7 или 8 раз - время выполнения для перераспределения - это несколько раз достаточно мало, чтобы беспокоиться, но вы, скорее всего, полностью превысите необходимый размер.

Ответ 3

Если бы вы выделили блок памяти необычного размера, тогда, когда этот блок будет освобожден (либо потому, что вы измените его размер, либо получите GC'd), в памяти будет отверстие необычного размера, которое может вызвать головные боли для менеджера памяти. Поэтому обычно предпочитают распределять память по двум. В некоторых случаях основной менеджер памяти будет давать вам только блоки определенных размеров, и если вы запросите странный размер, округлите его до следующего большего размера. Таким образом, вместо того, чтобы запрашивать 470 единиц, возвращая 512 в любом случае, а затем снова изменяя размер, как только вы используете все 470, о которых вы просили, возможно, просто попросите 512 начать с.

Ответ 4

Любое кратное является компромиссом. Сделайте его слишком большим, и вы потеряете слишком много памяти. Сделайте его слишком маленьким, и вы потратите много времени на перераспределение и копирование. Я думаю, что удвоение существует, потому что оно работает и очень легко реализовать. Я также видел проприетарную библиотеку, похожую на STL, которая использует 1,5 в качестве множителя для того же самого - я думаю, его разработчики считали, что удвоение тратит слишком много памяти.

Ответ 5

Если вы спрашиваете о реализации Java Vector и ArrayList, то это не обязательно удваивается при каждом расширении.

Из Javadoc для Vector:

Каждый вектор пытается оптимизировать управление хранением, поддерживая capacity и capacityIncrement. Емкость всегда не меньше размера вектора; он обычно больше, поскольку, поскольку компоненты добавляются к вектору, векторное хранилище увеличивается в кусках размером capacityIncrement. Приложение может увеличить пропускную способность вектора перед вставкой большого количества компонентов; это уменьшает количество инкрементного перераспределения.

Один из конструкторов для Vector позволяет указать начальный размер и прирост емкости для вектора. Класс Vector также предоставляет ensureCapacity(int minCapacity) и setSize(int newSize) для ручной настройки минимального размера вектора и для изменения размера вектора самостоятельно.

Класс ArrayList очень похож:

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

Приложение может увеличить емкость экземпляра ArrayList перед добавлением большого количества элементов, используя операцию обеспечения работоспособности. Это может уменьшить количество инкрементного перераспределения.

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

Ответ 6

Лично я считаю свой выбор произвольным. Мы могли бы использовать base e вместо base 2 (вместо того, чтобы удваивать только несколько размеров на (1 + e).)

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

База 2 является компромиссом.

Ответ 7

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