← Назад к вопросам
Какой объект получится при вызове map у ArrayList?
1.0 Junior🔥 281 комментариев
#Коллекции#Основы Java
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
# Какой объект получится при вызове map у ArrayList?
Краткий ответ
При вызове map() на ArrayList получается Stream, а не новый ArrayList. Это потому что map() — это метод потока (Stream), а не метод коллекции.
Как это работает
Шаг 1: Преобразование ArrayList в Stream
ArrayList<String> words = new ArrayList<>(Arrays.asList("hello", "world"));
// Вызов stream() преобразует ArrayList в Stream
Stream<String> stream = words.stream();
// map() вызывается на Stream, а не на ArrayList
Stream<Integer> lengthStream = stream.map(String::length);
// Результат map() — это Stream<Integer>, не ArrayList<Integer>!
Полный пример
ArrayList<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
// Вызов map() без явного stream() — ОШИБКА!
// numbers.map(n -> n * 2); // Ошибка компиляции!
// ArrayList не имеет метода map()
// Правильно: сначала stream(), потом map()
Stream<Integer> doubled = numbers.stream()
.map(n -> n * 2);
// Результат: Stream<Integer>
System.out.println(doubled); // java.util.stream.ReferencePipeline$2@...
Что такое Stream
Stream — это ленивая последовательность элементов, которая:
- Не хранит данные — это не контейнер, а конвейер обработки
- Ленивое вычисление — операции не выполняются пока не вызвана терминальная операция
- Одноразовое использование — Stream нельзя использовать дважды
- Функциональное программирование — поддерживает map, filter, reduce и т.д.
public class StreamNature {
public static void main(String[] args) {
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Создание Stream
Stream<Integer> stream = numbers.stream();
System.out.println(stream); // java.util.stream.ReferencePipeline$2@...
// Stream это не ArrayList
System.out.println(stream instanceof List); // false
System.out.println(stream instanceof Stream); // true
}
}
Цепочка операций (Pipeline)
Когда вы вызываете map(), создается pipeline операций:
List<String> words = Arrays.asList("apple", "banana", "cherry");
Stream<Integer> lengths = words.stream() // Источник: stream
.map(String::length) // Промежуточная операция
.filter(len -> len > 4) // Еще одна промежуточная
.distinct(); // И еще одна
// На этом этапе НИЧЕГО не вычислено!
// Stream хранит в себе описание операций, но не выполняет их
// Терминальная операция запускает вычисление
List<Integer> result = lengths.collect(Collectors.toList());
System.out.println(result); // [6] (только "banana" имеет длину > 4)
Промежуточные vs Терминальные операции
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
// Промежуточные операции (возвращают Stream):
.stream()
.map(n -> n * 2) // Возвращает Stream
.filter(n -> n > 4) // Возвращает Stream
.distinct() // Возвращает Stream
.sorted() // Возвращает Stream
// Все это не выполняется еще!
// Терминальная операция (запускает вычисление):
.forEach(System.out::println); // Выполняет все операции, возвращает void
// Другие терминальные операции:
.collect(Collectors.toList()) // Возвращает List
.reduce((a, b) -> a + b) // Возвращает Optional
.count() // Возвращает long
.findFirst() // Возвращает Optional
.anyMatch(n -> n > 10) // Возвращает boolean
.toArray() // Возвращает массив
Правильный способ: от Stream к ArrayList
ArrayList<String> words = new ArrayList<>(Arrays.asList(
"apple", "banana", "cherry"
));
// Вариант 1: Используя collect с ArrayList::new
ArrayList<Integer> lengths1 = words.stream()
.map(String::length)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(lengths1); // [5, 6, 6]
System.out.println(lengths1 instanceof ArrayList); // true
// Вариант 2: Используя Collectors.toList()
List<Integer> lengths2 = words.stream()
.map(String::length)
.collect(Collectors.toList());
// Может быть ArrayList или другой List (зависит от реализации)
System.out.println(lengths2); // [5, 6, 6]
// Вариант 3: Для Java 16+
List<Integer> lengths3 = words.stream()
.map(String::length)
.toList(); // Неизменяемый List
System.out.println(lengths3); // [5, 6, 6]
Практический пример
public class MapExample {
static class User {
String name;
int age;
User(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return name + " (" + age + ")";
}
}
public static void main(String[] args) {
ArrayList<User> users = new ArrayList<>(Arrays.asList(
new User("Alice", 25),
new User("Bob", 30),
new User("Charlie", 28)
));
// 1. Получить Stream из ArrayList
// 2. Применить map() для преобразования User -> String
// 3. Собрать результат в ArrayList
ArrayList<String> names = users.stream()
.map(user -> user.name) // map()
.collect(Collectors.toCollection(ArrayList::new)); // collect()
System.out.println(names); // [Alice, Bob, Charlie]
System.out.println(names instanceof ArrayList); // true
// Еще пример: map() с фильтром
ArrayList<String> adultNames = users.stream()
.filter(user -> user.age >= 25) // Фильтр
.map(user -> user.name) // Трансформация (map)
.collect(Collectors.toCollection(ArrayList::new));
System.out.println(adultNames); // [Alice, Bob, Charlie]
}
}
Почему Stream а не ArrayList
- Функциональный стиль — Stream поддерживает functional programming
- Оптимизация — можно комбинировать несколько операций
- Ленивое вычисление — операции откладываются до необходимости
- Параллелизм — легко переключиться на параллельный поток
- Декларативный код — описываешь ЧТО делать, а не КАК
// Декларативный стиль (Stream)
List<Integer> result1 = numbers.stream()
.filter(n -> n % 2 == 0) // Четные
.map(n -> n * n) // Возвести в квадрат
.collect(Collectors.toList());
// Императивный стиль (цикл)
List<Integer> result2 = new ArrayList<>();
for (Integer n : numbers) {
if (n % 2 == 0) {
result2.add(n * n);
}
}
// result1 и result2 содержат одинаковые данные
Частые ошибки
Ошибка 1: Вызов map() непосредственно на ArrayList
// ОШИБКА: ArrayList не имеет метода map()
ArrayList<String> words = new ArrayList<>();
words.map(s -> s.toUpperCase()); // Ошибка компиляции!
// Правильно:
words.stream().map(s -> s.toUpperCase()).collect(...);
Ошибка 2: Использование Stream дважды
Stream<Integer> stream = numbers.stream()
.map(n -> n * 2);
List<Integer> list1 = stream.collect(Collectors.toList());
List<Integer> list2 = stream.collect(Collectors.toList()); // ОШИБКА!
// IllegalStateException: stream has already been operated upon or closed
// Правильно: создавать новый Stream каждый раз
List<Integer> list1 = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
List<Integer> list2 = numbers.stream()
.map(n -> n * 2)
.collect(Collectors.toList());
Резюме
| Аспект | Описание |
|---|---|
| Метод map() | Принадлежит Stream, не ArrayList |
| Результат map() | Новый Stream (промежуточная операция) |
| Для ArrayList | Вызови .stream() первым |
| Для получения результата | Используй .collect() (терминальная операция) |
| Тип результата collect() | List, ArrayList, Set или другой |
При работе со Stream помни: map() возвращает Stream, а для получения конкретной коллекции (ArrayList, List) используй collect().