← Назад к вопросам

Какой объект получится при вызове 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 — это ленивая последовательность элементов, которая:

  1. Не хранит данные — это не контейнер, а конвейер обработки
  2. Ленивое вычисление — операции не выполняются пока не вызвана терминальная операция
  3. Одноразовое использование — Stream нельзя использовать дважды
  4. Функциональное программирование — поддерживает 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

  1. Функциональный стиль — Stream поддерживает functional programming
  2. Оптимизация — можно комбинировать несколько операций
  3. Ленивое вычисление — операции откладываются до необходимости
  4. Параллелизм — легко переключиться на параллельный поток
  5. Декларативный код — описываешь ЧТО делать, а не КАК
// Декларативный стиль (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().