JavaRush /Курсы /JAVA 25 SELF /List: ArrayList и LinkedList, основные операции

List: ArrayList и LinkedList, основные операции

JAVA 25 SELF
26 уровень , 1 лекция
Открыта

1. Класс ArrayList

В Java интерфейс List — это контракт: «Я — упорядоченная коллекция элементов, и у меня можно получить элемент по номеру (индексу)».

Ключевые свойства списка (List):

  • Элементы хранятся в определённом порядке (в отличие от множества).
  • Можно хранить дублирующиеся элементы (например: два раза "Вася").
  • Можно обратиться к элементу по индексу: первый элемент — индекс 0, второй — 1 и так далее.
  • Можно добавлять, удалять элементы в любое место списка.

Самая популярная реализация интерфейса List — это ArrayList. За кулисами он использует обычный массив и автоматически расширяется по мере роста данных.

Как создать ArrayList?

import java.util.ArrayList;
import java.util.List;

public class Example {
    public static void main(String[] args) {
        // Создаём список строк
        List<String> students = new ArrayList<>();
    }
}

Пояснение

  • List<String> — это переменная типа «список строк». Объявляем переменную через интерфейс, а создаём объект через конкретный класс (ArrayList).
  • Угловые скобки <String> задают тип элементов списка (дженерики, generics).

Основные методы ArrayList (и вообще любого List):

  • add(element) — добавить элемент в конец списка.
  • add(index, element) — вставить элемент по конкретному индексу.
  • get(index) — получить элемент по индексу.
  • set(index, element) — заменить элемент по индексу.
  • remove(index) — удалить элемент по индексу.
  • remove(Object) — удалить первый найденный элемент, равный переданному объекту.
  • size() — узнать количество элементов в списке.

Пример: Работаем со списком студентов

import java.util.ArrayList;
import java.util.List;

public class StudentListDemo {
    public static void main(String[] args) {
        List<String> students = new ArrayList<>();

        // Добавляем студентов
        students.add("Вася");
        students.add("Петя");
        students.add("Маша");

        // Выводим всех студентов
        System.out.println("Список студентов: " + students);

        // Получаем первого студента
        String first = students.get(0);
        System.out.println("Первый студент: " + first);

        // Меняем имя второго студента
        students.set(1, "Павел");
        System.out.println("После изменения: " + students);

        // Удаляем Машу
        students.remove("Маша");
        System.out.println("После удаления Маши: " + students);

        // Размер списка
        System.out.println("Всего студентов: " + students.size());
    }
}

Результат:

Список студентов: [Вася, Петя, Маша]
Первый студент: Вася
После изменения: [Вася, Павел, Маша]
После удаления Маши: [Вася, Павел]
Всего студентов: 2

Как это связано с нашим приложением?
Допустим, у нас есть приложение для учёта задач студента. Теперь можем хранить список задач как List<String>, а не как массив, и динамически добавлять новые задачи.

2. Класс LinkedList: когда важна скорость вставки и удаления

LinkedList — альтернативная реализация списка. Устроен как двусвязная цепочка: каждый узел знает предыдущий и следующий элементы. Как в составе поездов: вставить «вагон» посередине быстро и без перестройки всего состава.

Создание LinkedList

import java.util.LinkedList;
import java.util.List;

public class Example {
    public static void main(String[] args) {
        List<String> tasks = new LinkedList<>();
    }
}

Особенности LinkedList

  • Быстрая вставка и удаление в начале и середине списка.
  • Медленный доступ по индексу (чтобы найти 100-й элемент, нужно пройти по цепочке).
  • Подходит, если часто добавляете/удаляете элементы не только в конец, но и в начало или середину.

Пример: Используем LinkedList

import java.util.LinkedList;
import java.util.List;

public class TaskListDemo {
    public static void main(String[] args) {
        List<String> tasks = new LinkedList<>();

        tasks.add("Проснуться");
        tasks.add("Позавтракать");
        tasks.add("Пойти на пары");

        // Добавляем задачу в начало
        tasks.add(0, "Поставить будильник");

        System.out.println("Список задач: " + tasks);

        // Удаляем первую задачу (самую раннюю)
        tasks.remove(0);

        System.out.println("После удаления первой задачи: " + tasks);
    }
}

3. Сравнение ArrayList и LinkedList

Критерий
ArrayList
LinkedList
Основа Массив Двусвязный список
Быстрый доступ по индексу Да (O(1)) Нет (O(n))
Быстрая вставка/удаление в начале/середине Нет (O(n)) Да (O(1) — если есть ссылка)
Быстрая вставка/удаление в конце Да (обычно O(1)) Да (O(1))
Память Меньше тратит памяти Больше (доп. ссылки на соседей)
Типичные задачи Частый доступ по индексу Частые вставки/удаления

Простое правило:

  • Нужен быстрый доступ по номеру — используйте ArrayList.
  • Часто добавляете/удаляете в начало или середину — используйте LinkedList.

4. Типичные операции со списками

Перебор элементов списка

Обычный цикл for

for (int i = 0; i < students.size(); i++) {
    System.out.println("Студент #" + i + ": " + students.get(i));
}

Цикл for-each (самый популярный способ)

for (String name : students) {
    System.out.println("Имя: " + name);
}

Лямбда-выражение (Java 8+)

students.forEach(name -> System.out.println("Имя: " + name));

Поиск элементов

  • contains(element) — возвращает true, если элемент есть в списке.
  • indexOf(element) — возвращает индекс первого вхождения элемента, или -1, если не найден.
if (students.contains("Вася")) {
    System.out.println("Вася есть в списке!");
}
int index = students.indexOf("Вася");
System.out.println("Индекс Васи: " + index);

Очистка списка

clear() — удаляет все элементы.

students.clear();
System.out.println("Список после очистки: " + students);

5. Когда использовать ArrayList, а когда LinkedList?

Проще думать так: ArrayList хорош там, где нужно быстро обращаться к элементам по индексу, и список меняется нечасто. Например, длинный список пользователей или история сообщений — в основном чтение.

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

На практике чаще всего используют ArrayList. А LinkedList остаётся «инструментом на всякий случай»: лежит в ящике, но иногда оказывается именно тем, что нужно.

6. Типичные ошибки при работе со списками

Ошибка №1: Выход за пределы списка. Самая частая ситуация — обращение по несуществующему индексу. Если список из трёх элементов, а вы пишете students.get(5), получите IndexOutOfBoundsException. Перед доступом проверяйте size().

Ошибка №2: Удаление элемента во время перебора. При переборе через for-each и одновременном удалении элементов возникнет ConcurrentModificationException. Для сложных удалений используйте цикл по индексу или Iterator.

Ошибка №3: Неправильное сравнение объектов. Если вы храните собственные объекты (например, Student), методы contains и remove полагаются на equals. Если его не переопределить, сравнение будет по ссылке, а не по содержимому.

Ошибка №4: Использование сырых типов. Не пишите List list = new ArrayList() — всегда указывайте тип элементов: List<String> list = new ArrayList<>(). Дженерики защищают от ошибок и делают код понятнее.

1
Задача
JAVA 25 SELF, 26 уровень, 1 лекция
Недоступна
Расписание студента 🗓️✍️
Расписание студента 🗓️✍️
1
Задача
JAVA 25 SELF, 26 уровень, 1 лекция
Недоступна
Мой личный список фильмов "Что посмотреть" 🎬🍿
Мой личный список фильмов "Что посмотреть" 🎬🍿
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
German Malykh Уровень 31
19 октября 2025
Может быть полезно для понимания задачи "Мой личный список фильмов "Что посмотреть" 🎬🍿" и пункта - Требуется найти первое вхождение фильма "Матрица" в списке и вывести его порядковый номер на экран.

┌──────────────┐   ┌──────────────┐   ┌──────────────────┐   ┌──────────────┐
│ 1)           │   │ 2)           │   │ 3)               │   │ 4)           │
│   "Титаник"  │   │   "Матрица"  │   │   "Интерстеллар" │   │   "Матрица"  │
│     [0]      │   │     [1]      │   │       [2]        │   │     [3]      │
└──────────────┘   └──────────────┘   └──────────────────┘   └──────────────┘
                      ↑
                      └── первое вхождение "Матрица"
                          indexOf("Матрица") = 1
                          порядковый номер = 2

11 декабря 2025
Спасибо, никогда в жизни до этого бы не додумался
I'll kick them all Уровень 5
1 октября 2025

если нужно добавить новый вагон посередине, это делается быстро и без перестройки всего поезда. 
Ну на самом деле не так и быстро. Итак, для того чтоб добавить новый вагон в середину через LiinkedList, нужно: 1. Пройти до нного элемента с начала/конца списка - это O(n) через иттератор (явный цикл) что нифига не быстро. 2. Вставить вагон между соседями - O(1). Коллекция так себе работает и практически не используется, ибо ArrayList - 1. Прямой доступ по индексу O(1) 2. Быстрое копирование с помощью System.arraycopy - O(n) НО сильно оптимизировано, без явных циклов копируется блок памяти через memmove/memcpy из libc). Потому LinkedList возможно нужен только если нужно строго в конец/начало писать много записей. И не нужен доступ по индексу. Все.