Написание кода без использования collections framework было бы сумасшествием. Это замечательная часть Java с множеством изящных решений. Что более важно, в отличие от массивов, здесь вам не нужно заботиться о размерах. ArrayList будет расти до тех пор, пока не закончится память. Разработчику не нужно беспокоиться о начальном размере и ошибке ArrayIndexOutOfBoundsException. Но что, если поставлено условие следить за объемом памяти? Можем ли мы эффективно использовать память, работая с коллекциями?
Возникает стандартный вопрос: как инициализировать список? Нижеприведенный код работать не будет:
Если заглянуть в исходный код Java (версии 1.6)
Как можно видеть, по умолчанию создается список из 10 элементов. То есть мы смело можем использовать конструктор по умолчанию, когда в списке не планируется хранить более 10 элементов. Что случится, если попытаться добавить 11-й элемент? Ну, ничего плохого…, элемент успешно добавится. Посмотрите на метод:
Возникает стандартный вопрос: как инициализировать список? Нижеприведенный код работать не будет:
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 и так далее.
- Создавайте коллекцию только, когда нуждаетесь в ней, иначе инициализируйте null-значением или используйте Collections.EMPTY_LIST.
- Если известен точный необходимый размер, укажите его в конструкторе коллекции.
- Если есть уверенность в том, что количество элементов не превысит 10, свободно используйте конструктор по умолчанию.
- Риск, связанный с созданием коллекции нулевого размера, заключается в том, что частота создания новых массивов и копирования данных, может быть выше. Нужно очень тщательно продумать, так ли высока выгода от использования коллекций нулевого размера.
- Если инициализировали коллекцию слишком большого размера в начале метода и хочется его уменьшить, есть метод trimToSize().
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ