JavaRush /Блог /Архив info.javarush /new ArrayList(????) как и где лучше инициализировать
Vovan
22 уровень

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

Статья из группы Архив info.javarush
Написание кода без использования 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? Или с этим нужно смотреть в сторону массивов?
Спасибо!