ArrayList: примеры (JAVA)

Примеры работы с коллекцией ArrayList
Раздел: Коллекции (Collection Framework) - List
ArrayList

Общее описание класса ArrayList

Класс ArrayList из пакета java.util представляет собой реализованную на массиве динамическую реализацию интерфейса List. Применяется в случаях, когда требуется последовательная коллекция с быстрым доступом по индексу и частыми операциями добавления в конец. Характерные свойства: упорядоченность по вставке, допуск null-элементов, не синхронизирован по умолчанию.

Основные конструкторы и их параметры:

  • ArrayList() - создаёт пустой список с начальными внутренними настройками вместимости.
  • ArrayList(int initialCapacity) - выделяет внутренний массив указанной initialCapacity. При частом добавлении большого числа элементов выгодно задать ёмкость заранее.
  • ArrayList(Collection<? extends E> c) - создаёт список с элементами из переданной коллекции; ёмкость соответствует размеру коллекции.

Ключевые методы, сигнатуры, аргументы и возвращаемые значения:

  • boolean add(E e) - добавляет элемент в конец. Возвращает true (в ArrayList всегда true, если добавление прошло без исключений).
  • void add(int index, E element) - вставляет элемент по индексу; индекс должен быть в интервале [0, size]. При нарушении возникает IndexOutOfBoundsException.
  • E get(int index) - возвращает элемент по индексу; при неверном индексе выбрасывается IndexOutOfBoundsException.
  • E set(int index, E element) - заменяет элемент по индексу и возвращает старое значение.
  • E remove(int index) - удаляет элемент по индексу и возвращает удалённое значение.
  • boolean remove(Object o) - удаляет первое вхождение объекта и возвращает true, если элемент был найден и удалён.
  • int size() - возвращает текущее число элементов.
  • boolean isEmpty() - проверка на пустоту.
  • boolean contains(Object o) - поиск по равенству equals.
  • Iterator<E> iterator() и ListIterator<E> listIterator() - итераторы; при модификации списка вне итератора может возникнуть ConcurrentModificationException.
  • Object[] toArray() и <T> T[] toArray(T[] a) - преобразование в массив; при вызове вторым вариантом возвращается массив типа T.
  • boolean addAll(Collection<? extends E> c) и boolean addAll(int index, Collection<? extends E> c) - массовое добавление; возвращают true, если коллекция изменилась.
  • void clear() - удаляет все элементы, размер становится 0.
  • int indexOf(Object o) и int lastIndexOf(Object o) - поиск индекса первого и последнего вхождения, возвращают -1 если не найдено.
  • Java 8+ методы: void forEach(Consumer<? super E> action), boolean removeIf(Predicate<? super E> filter), void replaceAll(UnaryOperator<E> operator), void sort(Comparator<? super E> c) - модификации и обход с функциональными интерфейсами.
  • void ensureCapacity(int minCapacity) - запрос увеличения внутренней ёмкости, полезно для производительности при большом числе добавлений.
  • void trimToSize() - сокращение внутреннего массива до фактического размера.
  • Object clone() - поверхностная копия ArrayList; элементы не клонируются.

Исключения и гарантии: операции с индексами выбрасывают IndexOutOfBoundsException. Итераторы проверяют модификации за счёт fail-fast механизма, при некорректных параллельных изменениях возникает ConcurrentModificationException. ArrayList не обеспечивает потокобезопасность без внешней синхронизации.

Короткие примеры применения

Создание и базовые операции:

import java.util.ArrayList;

ArrayList list = new ArrayList<>();
list.add("one");
list.add("two");
list.add(1, "inserted");
System.out.println(list);
System.out.println(list.get(2));
list.set(0, "uno");
System.out.println(list.size());
[uno, inserted, two]
two
3

Удаление и проверка наличия:

ArrayList nums = new ArrayList<>(java.util.Arrays.asList(1,2,3,2));
boolean removed = nums.remove(Integer.valueOf(2));
System.out.println(removed);
System.out.println(nums);
true
[1, 3, 2]

Преобразование в массив и обратное добавление:

String[] arr = list.toArray(new String[0]);
System.out.println(java.util.Arrays.toString(arr));
ArrayList copy = new ArrayList<>(java.util.Arrays.asList(arr));
System.out.println(copy);
[uno, inserted, two]
[uno, inserted, two]

Сортировка и removeIf (Java 8+):

ArrayList words = new ArrayList<>(java.util.Arrays.asList("z", "a", "m"));
words.sort(java.util.Comparator.naturalOrder());
words.removeIf(s -> s.equals("m"));
System.out.println(words);
[a, z]

Похожие коллекции в Java и особенности

  • LinkedList - реализует List на двусвязном списке. Предпочтительнее при частых вставках/удалениях в середине коллекции, но доступ по индексу медленнее.
  • Vector - устаревший аналог ArrayList с синхронизацией на уровне методов. Возможна рекомендация в старом коде, но для новых проектов лучше использовать коллекции из java.util.concurrent при необходимости потокобезопасности.
  • CopyOnWriteArrayList - потокобезопасная реализация, оптимизирована для большого числа параллельных чтений и редких записей. Подходит для сценариев с малым числом модификаций.
  • Collections.unmodifiableList и List.of - возвращают неизменяемые представления; используются при передаче данных, которые не должны изменяться.
  • При выборе между ArrayList и LinkedList следует учитывать характер операций: частый доступ по индексу - ArrayList, частые вставки/удаления в середине - LinkedList.

Аналоги в других языках и отличия

Ниже примеры эквивалентных структур в разных языках и краткие замечания.

  • JavaScript - Array. Динамический, поддерживает push/pop/ splice. Отличие: в JS массивы более гибкие, нет строго типизации.
  • let a = [1,2];
    a.push(3);
    a.splice(1,0,9);
    console.log(a);
    [1,9,2,3]
  • Python - list. Аналог ArrayList по поведению, динамический и допускает любые типы.
  • lst = [1,2]
    lst.append(3)
    lst.insert(1,9)
    print(lst)
    [1, 9, 2, 3]
  • C# - List<T> в System.Collections.Generic. Очень похож по API; методы Add, Insert, Remove, ToArray.
  • var list = new System.Collections.Generic.List<int>{1,2};
    list.Add(3);
    list.Insert(1,9);
    Console.WriteLine(string.Join(",", list));
    1,9,2,3
  • Go - срезы (slices). Поведение похоже, но управление ёмкостью и копированием отличается; append возвращает новый срез.
  • s := []int{1,2}
    s = append(s, 3)
    // вставка
    s = append(s[:1], append([]int{9}, s[1:]...)...)
    fmt.Println(s)
    [1 9 2 3]
  • PHP - обычные массивы (associative) и объект ArrayObject. PHP-массивы реализованы как хеш-таблица, поэтому поведение по скорости отличается.
  • $a = [1,2];
    array_splice($a, 1, 0, 9);
    print_r($a);
    Array
    (
        [0] => 1
        [1] => 9
        [2] => 2
    )
    
  • Lua - таблицы (table) используются и как массивы, и как ассоциативные массивы. Отсутствует встроенная проверка границ индексирования.
  • local t = {1,2}
    table.insert(t,2,9)
    for i,v in ipairs(t) do print(v) end
    1
    9
    2
  • SQL - прямого аналога нет; логические коллекции представляются наборами строк или массивными типами в СУБД (например, PostgreSQL ARRAY).

Типичные ошибки и примеры

Частые ошибки при работе с ArrayList и возможные последствия:

  • IndexOutOfBoundsException при доступе по неверному индексу:
ArrayList<String> a = new ArrayList<>();
a.add("x");
System.out.println(a.get(1));
Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 1, Size: 1
    at java.util.ArrayList.rangeCheckForGet(ArrayList.java:xxx)
    ...
  • ConcurrentModificationException при одновременной модификации списка во время обхода for-each:
ArrayList<Integer> nums = new ArrayList<>(java.util.Arrays.asList(1,2,3));
for (Integer n : nums) {
    if (n == 2) nums.remove(n);
}
System.out.println(nums);
Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:xxx)
    ...

Рекомендация: при необходимости удаления в цикле применять Iterator.remove() или collect-and-remove.

  • Неожиданные результаты при использовании Arrays.asList(...) - полученная коллекция имеет фиксированный размер и не поддерживает add/remove:
java.util.List<String> fixed = java.util.Arrays.asList("a","b");
fixed.add("c");
Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(AbstractList.java:xxx)
    ...

Избежание: обернуть в новый ArrayList, если требуется изменяемая копия.

Также возможны ClassCastException при некорректных приведениях типов при использовании сырых типов или небезопасных преобразований массива через toArray.

Изменения и дополнения в последних версиях Java

Класс ArrayList сохраняет стабильный API, но в Java 8 и выше появились методы, улучшающие удобство работы:

  • Java 8: добавлены forEach, removeIf, replaceAll, sort. Это упростило применение лямбда-выражений и функциональных операций.
  • Java 9+: фабричные методы List.of и иные улучшения коллекций влияют на сценарии создания списков (возвращают неизменяемые списки). ArrayList как реализация изменений API не претерпевала.
  • Оптимизации внутренней работы JVM и реализации коллекций происходили постепенно, но семантика методов ArrayList осталась обратносвместимой.

Расширенные и редкие сценарии использования

1) Оптимизация выделения памяти при большом числе добавлений:

Пример java
ArrayList<Integer> big = new ArrayList<>(1_000_000);
for (int i = 0; i < 1_000_000; i++) big.add(i);
System.out.println(big.size());
1000000

Комментарий: заранее заданная ёмкость уменьшает число перераспределений внутреннего массива.

2) Использование subList как представления части списка и подводные камни:

Пример java
ArrayList<String> src = new ArrayList<>(java.util.Arrays.asList("a","b","c","d"));
java.util.List<String> view = src.subList(1,3);
view.set(0, "B");
System.out.println(src);
view.add("x");
System.out.println(src);
src.add("y"); // модификация исходного списка нарушит view при дальнейших операциях
[a, B, c, d]
[a, B, c, x, d]
// последующие операции с view могут вызвать ConcurrentModificationException

Комментарий: subList возвращает представление, изменения синхронизируются; однако структурные изменения родителя могут привести к ошибкам при дальнейшей работе с подсписком.

3) Потокобезопасные варианты: синхронизация и альтернативы

Пример java
// синхронизация вручную
java.util.List<String> sync = java.util.Collections.synchronizedList(new ArrayList<><>());
// CopyOnWriteArrayList для сценариев read-mostly
java.util.concurrent.CopyOnWriteArrayList<String> cow = new java.util.concurrent.CopyOnWriteArrayList<>();
cow.add("a");
for (String s : cow) System.out.println(s);
a

Комментарий: CopyOnWriteArrayList копирует внутреннюю структуру при каждой записи, что даёт безопасность итераций, но дорого при частых модификациях.

4) shallow clone и клонирование элементов:

Пример java
ArrayList<MyObj> orig = new ArrayList<>();
orig.add(new MyObj("x"));
ArrayList<MyObj> cloned = (ArrayList<MyObj>) orig.clone();
cloned.get(0).setName("y");
System.out.println(orig.get(0).getName());
y

Комментарий: clone делает поверхностную копию; если требуется глубокое клонирование, требуется копировать элементы вручную.

5) Параллельные операции через Stream API и влияние на исходный список:

Пример java
ArrayList<Integer> nums = new ArrayList<>(java.util.Arrays.asList(5,3,1,4));
java.util.List<Integer> sorted = nums.stream().sorted().toList();
System.out.println(sorted);
// оригинал не изменяется
System.out.println(nums);
[1, 3, 4, 5]
[5, 3, 1, 4]

Комментарий: stream().sorted() возвращает новый список; для изменения исходного можно вызвать nums.sort(...).

6) Сортировка с кастомным компаратором и стабилизация:

Пример java
class Item { String id; int pr; Item(String i,int p){id=i;pr=p;} public String toString(){return id+":"+pr;}}
ArrayList<Item> items = new ArrayList<>();
items.add(new Item("a",2)); items.add(new Item("b",1)); items.add(new Item("c",2));
items.sort(java.util.Comparator.comparingInt((Item it) -> it.pr).thenComparing(it -> it.id));
System.out.println(items);
[b:1, a:2, c:2]

Комментарий: комбинация компараторов позволяет контролировать порядок при равных ключах.

7) Поведение роста внутреннего массива (алгоритм расширения):

При добавлении при нехватке места новая ёмкость обычно равна oldCapacity + (oldCapacity >> 1) (приблизительно 1.5x). Это влияет на использование памяти и количество копирований.

джава ArrayList function comments

En
ArrayList Resizable-array implementation of the List interface