JavaRush/Java блог/Архив 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, 18:58
Что-то мне не очень вот тут понятно. Я лично столкнулся с вот таким. Можете пояснить? При создании листа new ArrayList(10) его size все равно не 10, а 0, если в него не записан ни один элемент. Как его проинициализировать так, чтобы сразу после создания в нем было 10 null ссылок на Object? Или с этим нужно смотреть в сторону массивов?
Спасибо!
hubert
Уровень 41
27 августа 2014, 19:10
Думаю, ты путаешь capacity и size.
Просто почитай
Если коротко, то у ArrayList есть массив обьектов, а есть отдельно поле size, которое, естественно, показывает сколько обьектов на данный момент хранятся в данном ArrayList. А если там хоть 100500 ссылок на null, то это ровно 0 обьектов. Следовательно, size будет 0, хотя capacity будет 100500.
gromozeka
Уровень 8
27 августа 2014, 19:50
Спасибо большое, уже читаю! А пока читаю перефразирую вопрос. Почему код
List<String> test = new ArrayList<>(10);
test.add(3, "test string");
не компилируется с ошибкой OutOfBounds? Ведь capacity=10 должно позволять сделать запись в 3 элемент?
Еще раз спасибо!
ToxyGenn
Уровень 20
27 августа 2014, 21:51
Тут похожее тема обсуждалась
javatalks.ru/topics/17335
Амиро
Уровень 25
20 декабря 2019, 13:35
без создания нулевого, первого и второго элемента нельзя добавить третий