User Vovan
Vovan
22 уровень

new ArrayList(????) как и где лучше инициализировать

Статья из группы Архив info.javarush.ru
Написание кода без использования collections framework было бы сумасшествием. Это замечательная часть Java с множеством изящных решений. Что более важно, в отличие от массивов, здесь вам не нужно заботиться о размерах. ArrayList будет расти до тех пор, пока не закончится память. Разработчику не нужно беспокоиться о начальном размере и ошибке ArrayIndexOutOfBoundsException. Но что, если поставлено условие следить за объемом памяти? Можем ли мы эффективно использовать память, работая с коллекциями?
Возникает стандартный вопрос: как инициализировать список? Нижеприведенный код работать не будет:
public List getAllNames() { List names; if (/*необходимые условия выполнены*/) { names = new ArrayList(); /*заполнение списка*/ } return names; }Это приведет к ошибке компиляции: The local variable names may not have been initialized – локальная переменная names может быть неинициализирована. Спецификация Java требует, чтобы все локальные переменные (те, что существуют в стеке) были инициализированы соответствующими значениями. Вот так это можно сделать:
List names = null; List names = new ArrayList(); List names = new ArrayList(0); List names = new ArrayList(size);Вне всяких сомнений список должен быть инициализирован. Вы можете либо создать список в первой же строке, либо поступить умнее – использовать отложенную (ленивую) инициализацию, как мы сделали в методе getAllNames(). В таком случае список будет создан только при необходимости - если начальные условия не выполнены, список никогда не появится в куче. Тут мы подошли к главному вопросу: какой же размер мы должны задать у списка? Лучший вариант дать ему точный размер. Например:
public List getTopNames (int n) { List names = null; if ( /*необходимые условия выполнены*/) { names = new ArrayList(n); /*заполнение списка*/ } return names; }В данном случае ровно n имен должно быть возвращено, и, как следствие, создается коллекция из n элементов. Однако, точное значение n известно далеко не всегда. Даже в приведенном примере, что если n=1000, а доступно было бы лишь 100 имен? При работе с коллекциями наиболее популярен вызов конструктора по умолчанию.
names = new ArrayList();
Если заглянуть в исходный код Java (версии 1.6)
/** * Конструирует пустой список с указанной начальной емкостью. * * @param initialCapacity начальная емкость списка * @exception IllegalArgumentException если указанная начальная емкость отрицательна * */ public ArrayList(int initialCapacity) { super(); if (initialCapacity < 0) this.elementData = new Object[initialCapacity]; } /** * Конструирует пустой список с начальной емкостью, равной 10. */ public ArrayList() { this(10); }
Как можно видеть, по умолчанию создается список из 10 элементов. То есть мы смело можем использовать конструктор по умолчанию, когда в списке не планируется хранить более 10 элементов. Что случится, если попытаться добавить 11-й элемент? Ну, ничего плохого…, элемент успешно добавится. Посмотрите на метод: public Boolean add(E e) { ensureCapacity(size + 1); //увеличивает modCount!! elementData[size++] = e; return true; }Размером массива управляет метод ensureCapacity. public void ensureCapacity (int minCapacity) { modCount++; int oldCapacity = elementData.length; if (minCapacity > oldCapacity) { throw new IllegalArgumentException(“Illegal Capacity: ” + initialCapacity); Object oldData[] = elementData; int newCapacity = (oldCapacity * 3) / 2 + 1; if (newCapacity < minCapacity) newCapacity = minCapacity; // minCapacity обычно ближе к размеру, так что это беспроигрышно: elementData = Arrays.copyOf(elementData, newCapacity); } } Размер увеличивается в 1,5 раза. Создается новый массив, и элементы перемещаются в него. Если мы задали размер 10, то на 11-м элементе:
  • размер меняется на 10 * 3 / 2 + 1 = 16
  • следующее увеличение = 16 * 3 / 2 + 1 = 25
  • следующее увеличение = 25 * 3 / 2 + 1 = 39 и так далее.
В случае 100 элементов, JVM необходимо будет несколько раз создавать новый массив и копировать в него элементы. С учетом этого, если есть предположение о необходимом размере массива, лучше задавать начальную емкость близкой к нему. Вот несколько указаний к действию:
  1. Создавайте коллекцию только, когда нуждаетесь в ней, иначе инициализируйте null-значением или используйте Collections.EMPTY_LIST.
  2. Если известен точный необходимый размер, укажите его в конструкторе коллекции.
  3. Если есть уверенность в том, что количество элементов не превысит 10, свободно используйте конструктор по умолчанию.
  4. Риск, связанный с созданием коллекции нулевого размера, заключается в том, что частота создания новых массивов и копирования данных, может быть выше. Нужно очень тщательно продумать, так ли высока выгода от использования коллекций нулевого размера.
  5. Если инициализировали коллекцию слишком большого размера в начале метода и хочется его уменьшить, есть метод trimToSize().
Конечно, эти указания применимы при работе с коллекциями на основе массива, и все это не имеет смысла в случае связного списка. На самом деле эти проблемы вряд ли будут убийцами программы, но, если есть возможность сделать чуть лучше, почему бы ее не использовать. Есть ли у вас другие полезные указания? Найденные вами способы заставить вещи работать лучше? Или это все излишне? Что вы думаете? Оригинал статьи тут.
Комментарии (5)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
gromozeka Уровень 8
27 августа 2014
Что-то мне не очень вот тут понятно. Я лично столкнулся с вот таким. Можете пояснить? При создании листа new ArrayList(10) его size все равно не 10, а 0, если в него не записан ни один элемент. Как его проинициализировать так, чтобы сразу после создания в нем было 10 null ссылок на Object? Или с этим нужно смотреть в сторону массивов?
Спасибо!