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

Как напишешь команду по сбору ArrayList

1.0 Junior🔥 191 комментариев
#Stream API и функциональное программирование#Коллекции

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Сбор результатов в ArrayList с использованием Stream API

Вопрос о сборе данных в ArrayList — частый в интервью. Он проверяет понимание Stream API и методов сбора данных в Java. За мой опыт я видел множество подходов, от примитивных до оптимизированных.

Традиционный подход (до Java 8)

До появления Stream API сбор в ArrayList выглядел так:

List<String> result = new ArrayList<>();
for (String item : originalList) {
    if (item.length() > 3) {
        result.add(item.toUpperCase());
    }
}

Этот подход работает, но не очень элегантен и требует много кода.

Современный подход со Stream API и collect()

C Java 8+ используется метод collect() с Collectors:

List<String> result = originalList.stream()
    .filter(item -> item.length() > 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

Различные способы сбора в список

1. Collectors.toList()

Основной способ сбора в список:

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
List<Integer> evenNumbers = numbers.stream()
    .filter(n -> n % 2 == 0)
    .collect(Collectors.toList());
// Результат: [2, 4]

2. Collectors.toCollection()

Для явного указания типа коллекции:

List<Integer> result = numbers.stream()
    .filter(n -> n > 2)
    .collect(Collectors.toCollection(ArrayList::new));

// Или с другой коллекцией
LinkedList<Integer> linkedList = numbers.stream()
    .filter(n -> n > 2)
    .collect(Collectors.toCollection(LinkedList::new));

// Или с TreeSet
NavigableSet<Integer> treeSet = numbers.stream()
    .collect(Collectors.toCollection(TreeSet::new));

3. Stream.toList() (Java 16+)

Метод toList() напрямую на Stream — более современный подход:

List<Integer> result = numbers.stream()
    .filter(n -> n % 2 == 0)
    .toList(); // Возвращает неизменяемый список

Важно: toList() возвращает неизменяемый список (Collections.unmodifiable). Если нужен изменяемый, используйте collect().

4. Сбор с преобразованием (map)

List<String> fruits = Arrays.asList("apple", "banana", "orange");
List<Integer> lengths = fruits.stream()
    .map(String::length)
    .collect(Collectors.toList());
// Результат: [5, 6, 6]

5. Сбор с фильтрацией и преобразованием

List<String> words = Arrays.asList("hello", "world", "java", "stream");
List<String> result = words.stream()
    .filter(word -> word.length() > 4)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
// Результат: [HELLO, WORLD, STREAM]

Продвинутые техники сбора

1. FlatMap — сбор из вложенных структур

List<List<Integer>> nestedList = Arrays.asList(
    Arrays.asList(1, 2, 3),
    Arrays.asList(4, 5),
    Arrays.asList(6, 7, 8, 9)
);

List<Integer> flatList = nestedList.stream()
    .flatMap(List::stream)
    .collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5, 6, 7, 8, 9]

2. Сбор с условной логикой

List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

List<String> result = numbers.stream()
    .map(n -> n % 2 == 0 ? "EVEN:" + n : "ODD:" + n)
    .collect(Collectors.toList());
// Результат: [ODD:1, EVEN:2, ODD:3, EVEN:4, ...]

3. Сбор от объектов с фильтрацией полей

public class Person {
    private String name;
    private int age;
    
    // getter'ы...
}

List<Person> people = Arrays.asList(
    new Person("John", 25),
    new Person("Jane", 30),
    new Person("Bob", 28)
);

// Собрать только имена людей старше 25 лет
List<String> names = people.stream()
    .filter(p -> p.getAge() > 25)
    .map(Person::getName)
    .collect(Collectors.toList());
// Результат: [Jane, Bob]

4. Сбор с Skip и Limit

List<Integer> result = numbers.stream()
    .skip(2)  // Пропустить первые 2 элемента
    .limit(3) // Взять следующие 3
    .collect(Collectors.toList());
// Результат: [3, 4, 5]

5. Distinct и Sorted при сборе

List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3, 4, 5, 5);

List<Integer> uniqueSorted = numbers.stream()
    .distinct()
    .sorted()
    .collect(Collectors.toList());
// Результат: [1, 2, 3, 4, 5]

// Сортировка в обратном порядке
List<Integer> reverseSorted = numbers.stream()
    .distinct()
    .sorted(Collections.reverseOrder())
    .collect(Collectors.toList());
// Результат: [5, 4, 3, 2, 1]

6. Кастомный Collector

// Сбор в строку с разделителем
String result = fruits.stream()
    .collect(Collectors.joining(", "));
// Результат: "apple, banana, orange"

// Сбор в список с префиксом и суффиксом
String prettyResult = fruits.stream()
    .collect(Collectors.joining(", ", "[", "]"));
// Результат: "[apple, banana, orange]"

7. Группировка при сборе

Map<Integer, List<Person>> byAge = people.stream()
    .collect(Collectors.groupingBy(Person::getAge));
// Результат: {25: [John], 28: [Bob], 30: [Jane]}

8. Условное добавление элементов

List<Integer> result = new ArrayList<>();
result.addAll(
    numbers.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList())
);

// Или более элегантно с Optional
List<Integer> filtered = numbers.stream()
    .filter(n -> n > 3)
    .collect(Collectors.toList());

Производительность: когда использовать какой метод

// Для небольших списков (до 1000 элементов)
List<Integer> small = numbers.stream().collect(Collectors.toList());

// Для больших списков с известной размерностью
List<Integer> large = numbers.stream()
    .collect(Collectors.toCollection(() -> new ArrayList<>(numbers.size())));

// Для параллельных потоков
List<Integer> parallel = numbers.parallelStream()
    .collect(Collectors.toCollection(ArrayList::new));

Типичные ошибки

// НЕПРАВИЛЬНО: collect() на null stream
Stream<Integer> nullStream = null;
List<Integer> result = nullStream.collect(Collectors.toList()); // NPE

// ПРАВИЛЬНО: проверить перед сбором
List<Integer> result = optional
    .map(list -> list.stream()
        .collect(Collectors.toList()))
    .orElseGet(ArrayList::new);

// НЕПРАВИЛЬНО: забыть про null элементы
List<Integer> withNull = Arrays.asList(1, 2, null, 4);
List<Integer> result = withNull.stream().collect(Collectors.toList()); // [1, 2, null, 4]

// ПРАВИЛЬНО: фильтровать null
List<Integer> filtered = withNull.stream()
    .filter(Objects::nonNull)
    .collect(Collectors.toList()); // [1, 2, 4]

Заключение

Сбор в ArrayList — базовая операция в Stream API. Основные моменты:

  1. Используйте collect(Collectors.toList()) для сбора в список
  2. toList() (Java 16+) удобнее, но возвращает неизменяемый список
  3. toCollection() дает больше контроля над типом коллекции
  4. Комбинируйте filter(), map(), flatMap() для сложной логики
  5. Будьте осторожны с null значениями
  6. Для больших данных рассмотрите parallelStream()