Автор
Александр Мяделец
Руководитель команды разработчиков в CodeGym

ArrayList в Java

Статья из группы Java Developer
участников
При разработке часто бывает сложно предсказать, какого размера понадобятся массивы. Поэтому функция динамического выделения памяти во время работы программы необходима каждому языку программирования. Динамическим называется массив, размер которого может измениться во время исполнения программы. В Java для такой цели существует класс ArrayList.

Что такое класс ArrayList?

ArrayList — реализация изменяемого массива интерфейса List, часть Collection Framework, который отвечает за список (или динамический массив), расположенный в пакете java.utils. Этот класс реализует все необязательные операции со списком и предоставляет методы управления размером массива, который используется для хранения списка. В основе ArrayList лежит идея динамического массива. А именно, возможность добавлять и удалять элементы, при этом будет увеличиваться или уменьшаться по мере необходимости.

Что хранит ArrayList?

Только ссылочные типы, любые объекты, включая сторонние классы. Строки, потоки вывода, другие коллекции. Для хранения примитивных типов данных используются классы-обертки.

Конструкторы ArrayList

  1. ArrayList()

    Пустой конструктор с начальной емкостью внутреннего массива = 10.

    ArrayList<String> list = new ArrayList<>();

    В угловых скобках желательно указать тип хранимых значений. В примере выше — String.

  2. ArrayList(Collection <? extends E> c)

    Конструктор принимает другую коллекцию, создавая новый массив с элементами переданной коллекции:

    ArrayList<String> list2 = new ArrayList<>(list);

    Порядок элементов в новом списке будет совпадать с исходным.

  3. ArrayList(int initialCapacity)

    В качестве параметра конструктора выступает значения начального размера внутреннего массива.

    ArrayList<String> list2 = new ArrayList<>(10000);

    Если в массиве, который лежит в основе ArrayList, закончилось место при добавлении новых элементов, создается новый массив большего размера, и данные копируются в него. Если при написании кода заранее известно, что в массиве будет обрабатываться большое количество элементов, в целях оптимизации следует указать большее значение.

Методы ArrayList

    Ниже представлены основные методы ArrayList.

  • add(E e)

    Добавляет новый элемент в конец списка. Возвращает boolean-значение (true — успех, false — не добавлено):

    ArrayList<String> list = new ArrayList<>();
    list.add("Hello");
  • add(int index, E element)

    Добавляет элемент element в позицию index. При добавлении происходит сдвиг всех элементов справа от указанного индекса на 1 позицию вправо:

    list.add(0, "Amigo");

    Очень полезен, когда нужно вставить элемент в произвольное место списка, однако для частых операций вставки в начало и середину ArrayList может оказаться не очень удачным выбором — следует изучить LinkedList.

  • addAll(Collection <? extends E> collection)

    Добавление всех элементов коллекции collection в список в порядке их расположения в collection.

  • addAll(int index, Collection <? extends E> collection)

    Добавление всех элементов collection в список начиная с индекса index. При этом все элементы сдвинутся вправо на количество элементов в списке collection:

    ArrayList<String> secondList = new ArrayList<>();
    secondList.addAll(list);
    System.out.println("Первое добавление: " + secondList);
    secondList.addAll(1, list);
    System.out.println("Второе добавление в середину: " + secondList);

    Вывод:

    
    Первое добавление: [Amigo, Hello]
    Второе добавление в середину: [Amigo, Amigo, Hello, Hello]
    

    Методы addAll() также возвращают boolean-результат добавления элементов.

  • clear()

    Удаление всех элементов из списка.

  • clone()

    Возвращает объект-копию массива:

    ArrayList<String> copyOfSecondList = (ArrayList<String>) secondList.clone();
    secondList.clear();
    System.out.println(copyOfSecondList);

    Вывод:

    
    [Amigo, Amigo, Hello, Hello]
    

    Следует обратить внимание, что метод clone() возвращает Object, так что после его вызова потребуется сделать приведение к необходимому классу.

    При клонировании создается новый независимый объект. В примере показано, как очищение клонированного объекта не сказалось на составе его клона.

  • contains(Object o)

    Проверка наличие объекта в списке, возвращает boolean-значение.

    System.out.println(copyOfSecondList.contains("Hello"));
    System.out.println(copyOfSecondList.contains("Check"));

    Вывод:

    
    true
    false
    
  • ensureCapacity(int minCapacity)

    Увеличивает размер внутреннего массива, чтобы в него поместилось количество элементов, переданных в minCapacity. Если массив достаточно вместителен, никакие преобразования не производятся.

    Этот метод полезен, когда возникает потребность вместить большое количество элементов в несколько итераций. Например, при создании списка емкость его внутреннего массива — 10. При загрузке данных по сети они обрабатываются асинхронно порциями и результаты помещаются в массив. Если ожидается доставка 10 000 элементов, может быть неэффективно просто добавлять эти данные каждый раз: достаточно будет в начале обработки вызвать метод ensureCapaciry(10000) и записывать туда данные по мере необходимости.

  • forEach(Consumer<? super E> action)

    Обработать в цикле ArrayList можно стандартными способами, цикл for:

    // Первый способ
    for(int i = 0; i< secondList.size(); i++) {
       System.out.println(secondList.get(i));
    }
    И цикл for-each:
    // Второй способ
    for(String s : secondList) {
       System.out.println(s);
    }

    В классе ArrayList есть метод для обработки каждого элемента, который называется также, forEach. В качестве аргумента передается реализация интерфейса Consumer, в котором нужно переопределить метод accept():

    secondList.forEach(new Consumer<String>() {
       @Override
       public void accept(String s) {
           System.out.println(s);
       }
    });

    Вывод:

    
    Amigo
    Amigo
    Hello
    Hello
    

    Метод accept принимает в качестве аргумента очередной элемент того типа, который хранит в себе ArrayList. Пример для Integer:

    ArrayList<Integer> integerList = new ArrayList<>();
    integerList.forEach(new Consumer<Integer>() {
       @Override
       public void accept(Integer integer) {
           System.out.println(integer);
       }
    });

    Метод action() будет выполнен для каждого элемента.

  • get(int index)

    Возвращает элемент, который расположен в указанной позиции списка.

    Если index < 0 или index >= максимального количества элементов списка, будет выброшено исключение IndexOutOfBoundsException.

    Это основной метод получения элемента из списка, время извлечения элемента по индексу всегда будет одинаковым, независимо от размера ArrayList.

  • indexOf(Object o)

    Метод возвращает индекс первого вхождения элемента в списке. Если элемента не существует в списке, метод вернет -1.

  • isEmpty()

    Метод возвращает true, если список пустой, false в обратном случае.

    Если в списке содержатся только элементы null, метод вернет false. Иными словами, null элементы также учитываются этим методом.

  • iterator()

    Возвращает итератор для списка для последующего использования в цикле или при любой другой обработке.

    Итератор для ArrayList — fail-fast. Это значит, что если коллекция изменится во время итерации, будет выброшено исключение ConcurrentModificationException. Подробнее об fail-fast и его противоположности fail-safe можно почитать здесь.

  • lastIndexOf(Object o)

    Функционал метода похож на indexOf(), отличие в том, что возвращается индекс последнего элемента в списке.

    Если элемент не найден, также возвращает -1.

  • remove(int index)

    Удаление элемента в указанной позиции индекса. После удаления сдвигает все элементы влево для заполнения освободившегося пространства.

    Если index<0 или >= количество элементов списка, будет выброшено исключение IndexOutOfBoundsException. В результате метод возвращает элемент, который был удален.

  • remove(Object o)

    Метод удаляет из списка переданный элемент o. Если элемент присутствует в списке, он удаляется, а все элементы смещаются влево. Если элемент существует в списке и успешно удален, метод возвращает true, в обратном случае — false.

  • removeAll(Collection<?> c)

    Если необходимо удалить несколько элементов, не стоит делать это в цикле по условию: гораздо удобнее и безопаснее воспользоваться методом removeAll(). Он принимает коллекцию элементов, которая будет удалена из списка.

    Коллекция должна содержать элементы того же типа, которые хранит целевой список. В обратном случае будет выброшен ClassCastException. Метод вернет true, если список был изменен в результате вызова метода.

  • set(int index, E element)

    Замена элемента в указанной позиции index на переданный element. Индекс также должен быть больше нуля и меньше индекса последнего элемента, иначе будет выброшено исключение IndexOutOfBoundsException.

  • size()

    Лучший способ (практически единственный) для того, чтобы узнать размер массива.

  • sort(Comparator<? super E> c)

    Сортировка списка по заданному правилу. Правило сортировки представляет собой реализованный интерфейс Comparator с переопределенным методом compareTo().

    Переопределение нужно, если коллекция содержит объекты собственного класса. При работе со стандартными классами (Integer, String и так далее) переопределение compareTo() требуется только для нестандартной сортировки.

  • toArray()

    Превращает список в фиксированный массив. Обратите внимание, что метод возвращает массив объектов (Object[]). Если необходимо привести список в массив объектов определенного типа, в качестве параметра в метод можно передать массив, куда будут перемещены элементы списков.

    Пример:

    String[] array = new String[secondList.size()];
    secondList.toArray(array);
    for(int i = 0; i< array.length; i++) {
       System.out.println(array[i]);
    }

    Вывод:

    
    Amigo
    Amigo
    Hello
    Hello
    
Методы ArrayList в Java изучаются на курсе JavaRush. Первое знакомство происходит на седьмом уровне квеста Java Syntax, на лекции “Класс ArrayList”. На этом же уровне есть подборки задач — раз и два, в которых нужно использовать методы ArrayList, приведены дополнительные примеры работы с ArrayList и дженериками, а также объясняется разница между ArrayList и LinkedList. Это обширная тема изучения, поэтому в том или ином виде к Arraylist в Java (методы этого класса — лишь часть всего массива знаний, в который стоит углубиться) на курсе возвращаются и на следующих уровнях обучения — Core, Collections, Multithreading. Мы верим, что ежедневная практика написания кода — главный ключ к успеху в программировании. Поэтому JavaRush на 80% состоит из практических задач, мини проектов, задач-игр. Все это — сотни часов кодинга, которые помогут прокачать скилл.

Ссылки на дополнительное чтение

  1. Подробная статья о динамических массивах, а точнее — об ArrayList и LinkedList, которые выполняют их роль в языке Java.
  2. Статья об удалении элементов из списка ArrayList.
  3. Лекция о работе с ArrayList в схемах и картинках.
Комментарии (17)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Иван
Уровень 23
29 мая 2023, 11:56
А как создать лист, не указав класс внутренних объектов? Например мне надо написать метод универсальный что для int что для String.
Lir
Уровень 14
9 июня 2023, 22:33
вроде можно использовать var. Но лучше же просто использовать несколько массивов, ведь обрабатывать значения не получится одинаково - будут постоянно появляться ошибки типа IllegalArgumentException или NumberFormatException.
Anomalous-Spectrum
Уровень 16
19 августа 2023, 20:39
Согласен с товарищем выше. Для разных целей всегда лучше использовать отдельные структуры данных. А по поводу вашего вопроса, если буквально: 1. Без указания класса внутренних объектов - это базовый или "сырой" ArrayList со стёртым параметром типа (это относилось к версии Java 5.0, когда обобщённых классов ещё не было). Данный класс оперирует объектами Object.
ArrayList list = new ArrayList();
2. И что вы понимаете под фразой "универсальный метод"? Смотря для чего. Допустим, вывести данные того же типа int и String можно и с типом Object. А вот сложить их к примеру уже не выйдет. Можно сказать, что в таком случае для такого списочного массива будут работать лишь основные методы класса Object. Но в любом случае обобщение до глобального типа Object - не самая лучшая идея и не всегда она даёт тот результат, который ожидается.
Дмитрий
Уровень 29
9 марта 2022, 06:06
Вероятно, в предложении перед пунктом с описанием метода get(int index), указали неверно метод - должен быть accept(), по факту action().
Рустем
Уровень 24
10 августа 2021, 10:26
почему при попытке клонирования как в статье, получаю clone() has protected access in java.lang.Object ? или там какие-то действия за кадром остались (при этом пробовал и чтобы класс имплементировал Clonable и проброс ошибки throws CloneNotSupportedException) ?
Steve Stifler
Уровень 14
1 июня 2021, 05:12
Объясните пожалуйста: "add(E e) Добавляет новый элемент в конец списка. Возвращает boolean-значение.". Куда оно возвращает значение? Оно же просто добавляет элемент! Часто вижу, что что-то возвращается, но не могу это понять и представить в голове, как это работает?
HKbot
Уровень 14
Expert
20 июня 2021, 11:51
Метод совершает какое-то действие с переданным ему аргументом, в нашем случае добавляет переданный элемент в список. Далее, метод возвращает вам результат операции добавления. True в случае успеха или False в случае неудачи. Вы не обязаны что-то делать с вернувшимся результатом, но если вам впоследствии нужно будет проверить, добавился элемент в список или нет, вы всегда сможете обработать вернувшийся результат.
🦔 Виктор веду учебный тг-канал в t.me/Javangelion Expert
26 сентября 2020, 03:20
Спасибо, отличная статья, доступно всё изложено и помогает решить соответствующие задачи. Законспектировал к себе на канал : ) -- Канал в телеге про Java и Android, в котором есть книги для скачивания, статьи, видеоуроки, чат для обмена знаниями и моральной поддержки : ) Давайте учиться вместе: @LetsCodeIt p. s. Мой личный телеграм канал вкатывальщика в прогерство: @SefoNotasi
Viktor Vdovichenko
Уровень 0
19 мая 2020, 21:12
Сортировка списка по заданному правилу. Правило сортировки представляет собой реализованный интерфейс Comparator с переопределенным методом compareTo(). А я думаю, что речь идёт об интерфейсе Comparable. Все встроенные типы данных поддерживают этот интерфейс. Давайте откроем класс java.lang.Integer. Мы видим, что этот класс реализует интерфейс java.util.Comparable. Вот доказательство: public final class Integer extends Number implements Comparable<Integer>
Sherlock0404
Уровень 28
9 мая 2020, 13:07
Метод action() будет выполнен для каждого элемента. Точно? Мне казалось речь идет об accept()...
Liudmyla Diachenko
Уровень 0
4 мая 2020, 16:01
import java.util.*; import java.lang.*; import java.io.*; class SplitString { ArrayList<String> even = new ArrayList<String>(); ArrayList<String> uneven = new ArrayList<String>(); public void evenOrUneven() { while(aStorage.size() > 0) { String word = aStorage.poll(); if(word.length() % 2 == 0) { even.add(word); System.out.println("Liczba parzysta znaków \"a\": " + word); } else { uneven.add(word); System.out.println("Liczba nieparzysta znaków \"a\": " + word); } } } } class Kodilla{ public static void main (String[] args){ ArrayDeque<String> aStorage = new ArrayDeque<String>(); Random randomGenerator = new Random(); for (int n=0; n<50; n++) { String result = ""; int randomNumber = randomGenerator.nextInt(50); for (int i = 0; i < randomNumber + 1 ; i++){ result = result + "a"; } aStorage.add(result); } SplitString spliter = new SplitString(); spliter.evenOrUneven(aStorage); } } В чем проблема? Как решить?
Дмитрий
Уровень 10
3 мая 2020, 04:36
Да, не плохо было бы еще рассказать о том, как копировать элементы внутри массива
Александр Плохой Senior Java Developer в freelance
17 марта 2020, 07:05
ошибка в строке "Метод action() будет выполнен для каждого элемента" - видимо, не action(), а accept()