2014-01-31 5 views
3

От a recently posted question Я наткнулся на ArrayList#trimToSize(), который уменьшает размер поддерживающей матрицы до текущего размера коллекции.Использование метода ArrayList # trimToSize()?

Цитирование JavaDoc

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

И Javadoc говорит, что приложение может использовать, чтобы уменьшить объем памяти массива поддержки. Если я не ошибаюсь, этот метод не будет полезен для небольших размеров, так как стоимость некоторых ссылок не повредит.

Но из-за алгоритм, используемого Список_массивами int newCapacity = (oldCapacity * 3)/2 + 1; в 1,6 и int newCapacity = oldCapacity + (oldCapacity >> 1); в 1.7, при добавлении нового элемента, если oldcapacity велико, то это создаст новый массив подкладочного с вышеприведенным алгоритмом и может выделять много ненужного пространства, если только один элемент добавляется после динамического расширения.

Является ли мое обоснование методом правильным или есть некоторые другие приложения к нему?

+0

Алгоритм изменения размера (по крайней мере, в Java 7) - это 'int newCapacity = oldCapacity + (oldCapacity >> 1);'. –

+0

@JeroenVannevel Спасибо, я добавил пункт. –

+0

Downvoter хочет прокомментировать? –

ответ

8

Да, массив поддержки увеличивается на ~ 50%, когда он заполнен. Например, программа ниже добавляет 1 миллион записей, вызывает trimToSize, затем добавляет одну запись. Длина базы поддержки составляет 1,2 м после добавления записей, 1 м после обрезки и 1,5 м после добавления одного элемента.

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

ArrayList<Integer> list = new ArrayList<>(); 
Field e = list.getClass().getDeclaredField("elementData"); 
e.setAccessible(true); 
for (int i = 0; i < 1_000_000; i++) { 
    list.add(i); 
} 
System.out.println(((Object[]) e.get(list)).length); //1215487 
list.trimToSize(); 
System.out.println(((Object[]) e.get(list)).length); //1000000 
list.add(0); 
System.out.println(((Object[]) e.get(list)).length); //1500000 
+0

Угадайте, кто еще пишет ответ прямо сейчас, и с какой точки :) –

+1

@MarkoTopolnik Вы собираетесь утверждать, что метод бесполезен? ;-) – assylias

+0

@MarkoTopolnik вы, ребята, потрясающие;) assylias просто напишите ему чек, и он не будет чувствовать себя плохо: D –

1

Другая ситуация, когда мы добавляем элементы, а затем удаляем многие из них. Когда мы удаляем элементы, внутренний арай остается неизменным.

1

Ваша формула приводит к наихудшим издержкам только 50% пустых слотов. Обратите внимание, что минимальный размер Object составляет 24 байта по сравнению с 4 байтами для сжатого ООП в массиве. Накладные составляет всего

(0.5*4)/(24+4) == 1/14 == 7% 

, которые вряд ли когда-либо может быть стоит рассмотреть — и это худшем случае это может получить. В среднем это составляет половину накладных расходов в записях массива, и часто объекты намного больше.

Таким образом, единственный раз, когда было бы целесообразно позвонить trimToSize после массивного удаления из ранее огромного массива. Другими словами, почти никогда.

+0

+1 для последней точки, которая интересна (и остальная часть анализа, конечно!). Однако я думаю, что размер 'Object' составляет 12 байтов (может быть, добавлен до 16), а не 24. – assylias

+0

Также следует рассмотреть вопрос о том, что если я делаю так много абзацев, я бы вообще не использовал ArrayList. Я бы использовал Linkedlist. Имеет смысл? –

+0

@NarendraPathai Вероятно, нет ... 'LinkedList' довольно плох в отношении накладных расходов для каждого элемента, поэтому, когда' trimToSize' стоит рассмотреть, 'LinkedList' не может быть и речи. То, что я на самом деле делал в таком случае, является аналогом * копирующего сборщика мусора *: создайте новый 'ArrayList' и скопируйте оставшихся в живых. –

Смежные вопросы