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
| Критерий | |
|
|---|---|---|
| Основа | Массив | Двусвязный список |
| Быстрый доступ по индексу | Да (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<>(). Дженерики защищают от ошибок и делают код понятнее.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ