5 способов создания списков в Java: примеры для разработчиков
Для кого эта статья:
- Новички в программировании на Java
- Студенты и курсанты, изучающие основы Java-разработки
Практикующие разработчики, желающие расширить свои знания о коллекциях Java
Работа со списками — одна из базовых операций в программировании на Java, которая встречается буквально в каждом втором проекте. Независимо от того, разрабатываете ли вы мобильное приложение, веб-сервис или программу для обработки данных, навык эффективной работы со списками критически важен. Для новичка многообразие способов создания списков может оказаться запутанным лабиринтом. Давайте разберем 5 основных способов создания списков в Java с рабочими примерами, которые помогут вам выбрать оптимальный вариант для ваших задач. 🚀
Если вы только начинаете осваивать Java и хотите получить структурированные знания от опытных практиков, обратите внимание на Курс Java-разработки от Skypro. На курсе вы не только изучите различные способы работы со списками и другими коллекциями, но и получите практические навыки, которые востребованы на рынке. Менторы с опытом работы в крупных IT-компаниях помогут избежать типичных ошибок новичков и ускорят ваш путь к первой работе.
Что такое списки и зачем они нужны в Java
Список (List) в Java — это упорядоченная коллекция элементов, которая позволяет хранить данные и манипулировать ими. В отличие от массивов, списки имеют динамический размер и предоставляют богатый набор методов для работы с элементами.
Антон Петров, Java-разработчик в финтех-стартапе Когда я только начинал работать с Java, я постоянно использовал массивы для всего. Однажды мне потребовалось добавить элемент в середину уже заполненного массива. Я потратил несколько часов, пытаясь реализовать это "велосипедным" способом. Руководитель команды увидел мои мучения и просто показал, как сделать то же самое с ArrayList в три строки кода. Этот момент стал поворотным в моем понимании Java-коллекций. Теперь я автоматически задаю себе вопрос: "А нужен ли здесь массив, или лучше использовать список?" перед началом работы над новой задачей.
Основные преимущества списков перед обычными массивами:
- Динамический размер — нет необходимости заранее указывать фиксированную длину
- Богатый API с множеством полезных методов (add, remove, contains, indexOf и др.)
- Различные имплементации для разных сценариев использования
- Поддержка обобщенных типов (generics) для обеспечения типобезопасности
В Java существует несколько реализаций интерфейса List, каждая со своими особенностями и областями применения:
| Тип списка | Особенности | Когда использовать |
|---|---|---|
| ArrayList | Быстрый произвольный доступ, медленные вставки/удаления в середине | Для частого доступа по индексу и редких изменений |
| LinkedList | Быстрые вставки/удаления, медленный произвольный доступ | Для частого добавления/удаления элементов |
| Vector | Синхронизированный, потокобезопасный | Для многопоточных приложений (устаревший) |
| CopyOnWriteArrayList | Потокобезопасный, оптимизирован для частого чтения | Для многопоточных приложений с преобладанием операций чтения |
В повседневной разработке чаще всего используется ArrayList благодаря его универсальности и эффективности для большинства задач. 📊

Создание ArrayList: простейшие методы инициализации
ArrayList — наиболее часто используемая реализация интерфейса List в Java. Давайте рассмотрим основные способы создания и инициализации ArrayList.
- Создание пустого списка:
// Создание пустого списка строк
List<String> names = new ArrayList<>();
Этот способ создает пустой ArrayList с начальной емкостью по умолчанию (10 элементов). Когда вы добавляете элементы, список автоматически увеличивает свой размер.
- Создание списка с заданной начальной емкостью:
// Создание пустого списка с начальной емкостью 20 элементов
List<Integer> numbers = new ArrayList<>(20);
Если вы примерно знаете, сколько элементов будет в списке, установка начальной емкости может повысить производительность, так как список не будет тратить ресурсы на динамическое расширение.
- Создание списка с предварительно заполненными элементами:
// Инициализация с существующей коллекцией
List<String> otherNames = new ArrayList<>();
otherNames.add("Анна");
otherNames.add("Иван");
List<String> allNames = new ArrayList<>(otherNames);
- Создание и заполнение списка с помощью анонимного блока инициализации:
// Использование анонимного блока инициализации
List<Integer> scores = new ArrayList<>() {{
add(85);
add(90);
add(78);
}};
⚠️ Хотя этот метод может показаться удобным, он имеет недостатки: создает дополнительный анонимный класс и может вызывать утечки памяти. Не рекомендуется использовать его в производственном коде.
- Создание списка с помощью коллекций-фабрик (Java 8+):
// Преобразование массива в список и создание ArrayList на его основе
String[] fruitsArray = {"Яблоко", "Банан", "Апельсин"};
List<String> fruitsList = new ArrayList<>(Arrays.asList(fruitsArray));
Выбор метода инициализации зависит от конкретной ситуации:
- Когда не знаете начального размера — используйте пустой конструктор
- Когда знаете примерный размер — указывайте емкость
- Когда нужно скопировать данные из другой коллекции — используйте конструктор с коллекцией
Использование Arrays.asList() для быстрой конвертации
Метод Arrays.asList() — один из самых удобных способов быстрого создания списка на основе массива или набора значений. Этот метод доступен в классе java.util.Arrays и часто используется для инициализации списков с предопределенными значениями. 🔄
// Создание списка из отдельных элементов
List<String> colors = Arrays.asList("Красный", "Зеленый", "Синий");
// Создание списка из массива
Integer[] numbers = {1, 2, 3, 4, 5};
List<Integer> numbersList = Arrays.asList(numbers);
Важно понимать несколько ключевых особенностей Arrays.asList():
- Возвращает фиксированный по размеру список, поддерживаемый массивом
- Вы не можете добавлять или удалять элементы (вызов методов add/remove приведет к UnsupportedOperationException)
- Изменения в полученном списке отражаются в исходном массиве, и наоборот
- Список имеет фиксированный размер, равный размеру исходного массива
Если вам нужен изменяемый список, вы можете создать новый ArrayList на основе результата Arrays.asList():
// Создание изменяемого списка на основе Arrays.asList()
List<String> mutableColors = new ArrayList<>(Arrays.asList("Красный", "Зеленый", "Синий"));
mutableColors.add("Желтый"); // Теперь это работает!
Мария Соколова, преподаватель программирования На одном из моих занятий студент столкнулся с загадочным исключением. Он пытался добавить элемент в список, созданный с помощью Arrays.asList(), и получал UnsupportedOperationException. Это стало отличным учебным моментом! Я объяснила всей группе, что Arrays.asList() возвращает специальный вид списка с фиксированным размером. Мы разобрали внутреннюю реализацию этого метода, и теперь студенты используют конструкцию
new ArrayList<>(Arrays.asList(...))всякий раз, когда им нужен изменяемый список. Такие практические ошибки запоминаются лучше любой теории.
Давайте сравним различные способы создания списков на основе массивов:
| Метод | Результат | Изменяемый размер | Связь с исходным массивом |
|---|---|---|---|
| Arrays.asList(array) | Фиксированный список | Нет | Да (отражает изменения) |
| new ArrayList<>(Arrays.asList(array)) | Полноценный ArrayList | Да | Нет (независимая копия) |
| List.of(array) (Java 9+) | Неизменяемый список | Нет | Нет |
| Stream.of(array).collect(Collectors.toList()) | ArrayList | Да | Нет |
Особенность работы с примитивными типами:
// Не работает напрямую с примитивными типами
// int[] primitiveArray = {1, 2, 3};
// List<Integer> list = Arrays.asList(primitiveArray); // Ошибка компиляции!
// Правильный подход для примитивных типов
int[] primitiveArray = {1, 2, 3};
List<Integer> list = Arrays.stream(primitiveArray)
.boxed()
.collect(Collectors.toList());
Неизменяемые коллекции: методы List.of() и List.copyOf()
Начиная с Java 9, в JDK появились удобные фабричные методы для создания неизменяемых (immutable) коллекций, включая списки. Методы List.of() и List.copyOf() предоставляют элегантный способ создания списков, которые нельзя модифицировать после создания. 🔒
Использование List.of() для создания неизменяемого списка:
// Создание неизменяемого списка строк
List<String> fruits = List.of("Яблоко", "Груша", "Апельсин");
// Создание пустого неизменяемого списка
List<Integer> emptyList = List.of();
// Создание списка с одним элементом
List<Double> singletonList = List.of(3.14);
List.of() имеет перегруженные версии для разного количества параметров (до 10), а также версию с varargs для произвольного количества элементов.
Метод List.copyOf() создает неизменяемый список, содержащий элементы указанной коллекции:
// Создание изменяемого списка
List<String> mutableList = new ArrayList<>();
mutableList.add("Один");
mutableList.add("Два");
mutableList.add("Три");
// Создание неизменяемой копии
List<String> immutableCopy = List.copyOf(mutableList);
Ключевые особенности неизменяемых списков:
- Нельзя добавлять, удалять или заменять элементы (выбрасывается UnsupportedOperationException)
- Нельзя вставить null (выбрасывается NullPointerException)
- Структура оптимизирована для неизменяемости, что дает выигрыш в производительности и потреблении памяти
- Потокобезопасны без необходимости синхронизации
Когда использовать неизменяемые списки:
- Константы и предопределенные наборы значений
- Защита данных от нежелательных изменений
- Многопоточные сценарии, где не требуется изменение коллекции
- Кэширование данных
- Возвращаемые значения методов, когда не хотите, чтобы вызывающий код мог изменить ваши внутренние данные
Сравнение производительности и характеристик разных способов создания неизменяемых списков:
// До Java 9
List<String> unmodifiableList = Collections.unmodifiableList(new ArrayList<>(Arrays.asList("A", "B", "C")));
// С Java 9+
List<String> immutableList = List.of("A", "B", "C");
Новые методы не только сокращают количество кода, но и обеспечивают лучшую производительность, поскольку оптимизированы специально для создания неизменяемых коллекций. 💪
Специализированные списки: LinkedList и Vector
Помимо ArrayList, в Java существуют и другие реализации интерфейса List, которые оптимизированы для специфических сценариев использования. Рассмотрим два наиболее распространенных варианта: LinkedList и Vector. 🔗
LinkedList — двусвязный список, который оптимизирован для операций вставки и удаления элементов, особенно в середине списка:
// Создание пустого связного списка
LinkedList<String> linkedList = new LinkedList<>();
// Добавление элементов
linkedList.add("Первый");
linkedList.add("Второй");
// Добавление в начало списка (быстрая операция для LinkedList)
linkedList.addFirst("Нулевой");
// Добавление в конец списка
linkedList.addLast("Последний");
// Получение первого и последнего элементов (быстрая операция)
String first = linkedList.getFirst();
String last = linkedList.getLast();
LinkedList также реализует интерфейсы Deque и Queue, что делает его подходящим для реализации очередей и стеков:
// Использование LinkedList как двусторонней очереди
LinkedList<Integer> deque = new LinkedList<>();
deque.push(1); // Добавление в начало
deque.push(2); // Добавление в начало
int top = deque.pop(); // Извлечение из начала (LIFO – стек)
// Использование как очереди
deque.offer(3); // Добавление в конец
deque.offer(4); // Добавление в конец
int first = deque.poll(); // Извлечение из начала (FIFO – очередь)
Vector — устаревшая реализация динамического массива, которая синхронизирована и потокобезопасна:
// Создание Vector с начальной емкостью 10
Vector<Double> vector = new Vector<>();
// Создание Vector с заданной емкостью
Vector<Double> vectorWithCapacity = new Vector<>(20);
// Создание Vector с емкостью и инкрементом роста
// (при переполнении размер увеличивается на 5)
Vector<Double> vectorWithIncrement = new Vector<>(10, 5);
// Добавление элементов
vector.add(1.1);
vector.add(2.2);
vector.add(3.3);
// Доступ по индексу
double element = vector.get(1); // 2.2
Сравнение характеристик различных реализаций List:
| Характеристика | ArrayList | LinkedList | Vector |
|---|---|---|---|
| Внутренняя структура | Динамический массив | Двусвязный список | Динамический массив |
| Доступ по индексу | O(1) – быстрый | O(n) – линейный | O(1) – быстрый |
| Вставка/удаление в середине | O(n) – медленный | O(1) – быстрый* | O(n) – медленный |
| Вставка/удаление в начале | O(n) – медленный | O(1) – быстрый | O(n) – медленный |
| Потокобезопасность | Нет | Нет | Да (синхронизированный) |
| Использование памяти | Экономное | Повышенное | Экономное |
- При условии, что у вас уже есть ссылка на узел, куда нужно вставить или откуда нужно удалить элемент. Поиск узла занимает O(n).
Рекомендации по выбору реализации:
- ArrayList: используйте в большинстве случаев, особенно когда преобладает доступ по индексу
- LinkedList: используйте, когда часто добавляете или удаляете элементы в начале или середине списка, или когда требуется реализация очереди или стека
- Vector: используйте только в устаревшем коде или когда требуется простая синхронизированная коллекция (хотя в новом коде лучше использовать Collections.synchronizedList() или CopyOnWriteArrayList)
Пример создания потокобезопасного списка в современном Java:
// Предпочтительнее, чем Vector
List<String> synchronizedList = Collections.synchronizedList(new ArrayList<>());
// Для сценариев "много чтений, мало записей"
List<String> concurrentList = new CopyOnWriteArrayList<>();
Выбор правильной реализации List может значительно повлиять на производительность вашего приложения, особенно при работе с большими объемами данных или в критических участках кода. 📈
Работа со списками в Java — это не просто техническое умение, а способ мышления о структурах данных. Выбор правильной реализации и метода инициализации может значительно повлиять на производительность и читаемость вашего кода. ArrayList подойдет для большинства задач, LinkedList поможет, когда важна скорость вставки, а неизменяемые коллекции обеспечат безопасность ваших данных. Освоив различные способы создания списков, вы сделаете значительный шаг вперед как Java-разработчик.