Для эффективного использования памяти инициализируйте ArrayList с предполагаемым размером через new ArrayList(size), используйте конструктор по умолчанию только для ≤10 элементов, применяйте ленивую инициализацию и помните — ArrayList увеличивается в 1.5 раза при превышении емкости. Теперь рассмотрим детально, как правильно работать с размерами коллекций: Написание кода без использования 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().
Конечно, эти указания применимы при работе с коллекциями на основе массива, и все это не имеет смысла в случае связного списка. На самом деле эти проблемы вряд ли будут убийцами программы, но, если есть возможность сделать чуть лучше, почему бы ее не использовать. Есть ли у вас другие полезные указания? Найденные вами способы заставить вещи работать лучше? Или это все излишне? Что вы думаете?