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

В какие коллекции собирал Stream

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

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

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

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

# Собрание Stream в коллекции с помощью Collectors

Stream в Java — это не коллекция, а способ обработки данных. Когда нужно собрать результаты Stream в коллекцию, используют Collectors. Это один из самых частых вопросов в собеседованиях.

Основной способ: collect() с Collectors

import java.util.stream.Collectors;
import java.util.*;

public class StreamCollectorsExample {
    
    // 1. Собрание в List (самый частый случай)
    @Test
    public void collectToList() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
        
        List<Integer> evenNumbers = numbers.stream()
            .filter(n -> n % 2 == 0)
            .collect(Collectors.toList()); // или toUnmodifiableList()
        
        System.out.println(evenNumbers); // [2, 4]
    }
    
    // 2. Собрание в Set
    @Test
    public void collectToSet() {
        List<Integer> numbers = Arrays.asList(1, 2, 2, 3, 3, 3);
        
        Set<Integer> uniqueNumbers = numbers.stream()
            .collect(Collectors.toSet()); // HashSet
        
        System.out.println(uniqueNumbers); // [1, 2, 3]
    }
    
    // 3. Собрание в конкретный Set (TreeSet, LinkedHashSet)
    @Test
    public void collectToSpecificSet() {
        List<Integer> numbers = Arrays.asList(3, 1, 2);
        
        TreeSet<Integer> sorted = numbers.stream()
            .collect(Collectors.toCollection(TreeSet::new));
        
        System.out.println(sorted); // [1, 2, 3] в порядке сортировки
    }
    
    // 4. Собрание в Map
    @Test
    public void collectToMap() {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie");
        
        // Ключ: имя, Значение: длина
        Map<String, Integer> nameLength = names.stream()
            .collect(Collectors.toMap(
                Function.identity(),     // ключ: само имя
                String::length           // значение: длина имени
            ));
        
        System.out.println(nameLength);
        // {Alice=5, Bob=3, Charlie=7}
    }
}

Разные типы коллекций

public class CollectorsTypesExample {
    
    List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
    
    // List — изменяемый список
    List<Integer> list = numbers.stream()
        .filter(n -> n > 2)
        .collect(Collectors.toList());
    // Результат: [3, 4, 5]
    
    // Unmodifiable List (Java 10+) — неизменяемый
    List<Integer> unmodifiableList = numbers.stream()
        .filter(n -> n > 2)
        .collect(Collectors.toUnmodifiableList());
    // Результат: [3, 4, 5] (read-only)
    
    // Set — уникальные элементы
    Set<Integer> set = numbers.stream()
        .filter(n -> n > 2)
        .collect(Collectors.toSet());
    // Результат: [3, 4, 5] (порядок не гарантирован)
    
    // LinkedHashSet — с сохранением порядка
    Set<Integer> linkedSet = numbers.stream()
        .filter(n -> n > 2)
        .collect(Collectors.toCollection(LinkedHashSet::new));
    // Результат: [3, 4, 5] (порядок сохранён)
    
    // TreeSet — отсортированный
    Set<Integer> treeSet = numbers.stream()
        .filter(n -> n > 2)
        .collect(Collectors.toCollection(TreeSet::new));
    // Результат: [3, 4, 5] (отсортировано)
    
    // Map — собрание в пары ключ-значение
    Map<Integer, String> map = numbers.stream()
        .collect(Collectors.toMap(
            n -> n,
            n -> "num_" + n
        ));
    // Результат: {1=num_1, 2=num_2, ...}
    
    // LinkedHashMap — Map с сохранением порядка
    Map<Integer, String> linkedMap = numbers.stream()
        .collect(Collectors.toMap(
            n -> n,
            n -> "num_" + n,
            (a, b) -> a,  // merge function (для дубликатов)
            LinkedHashMap::new
        ));
    
    // ConcurrentHashMap — потокобезопасный Map
    Map<Integer, String> concurrent = numbers.stream()
        .collect(Collectors.toMap(
            n -> n,
            n -> "num_" + n,
            (a, b) -> a,
            ConcurrentHashMap::new
        ));
}

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

Grouping By (группировка по критерию)

public class GroupingExample {
    
    @Test
    public void groupByExample() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 25),
            new Person("David", 30)
        );
        
        // Группировка по возрасту
        Map<Integer, List<Person>> byAge = people.stream()
            .collect(Collectors.groupingBy(Person::getAge));
        
        System.out.println(byAge);
        // {
        //   25=[Alice, Charlie],
        //   30=[Bob, David]
        // }
    }
    
    @Test
    public void groupingWithDownstream() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 25)
        );
        
        // Группировка по возрасту с подсчётом
        Map<Integer, Long> ageCount = people.stream()
            .collect(Collectors.groupingBy(
                Person::getAge,
                Collectors.counting()  // downstream collector
            ));
        
        System.out.println(ageCount);
        // {25=2, 30=1}
    }
    
    @Test
    public void groupingWithMapping() {
        List<Person> people = Arrays.asList(
            new Person("Alice", 25),
            new Person("Bob", 30),
            new Person("Charlie", 25)
        );
        
        // Группировка по возрасту, собрание имён
        Map<Integer, List<String>> namesByAge = people.stream()
            .collect(Collectors.groupingBy(
                Person::getAge,
                Collectors.mapping(
                    Person::getName,
                    Collectors.toList()
                )
            ));
        
        System.out.println(namesByAge);
        // {
        //   25=[Alice, Charlie],
        //   30=[Bob]
        // }
    }
}

class Person {
    private String name;
    private int age;
    
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    
    public String getName() { return name; }
    public int getAge() { return age; }
    
    @Override
    public String toString() { return name; }
}

Partitioning By (разбиение на две части)

public class PartitioningExample {
    
    @Test
    public void partitioningExample() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        
        // Разбиение на чётные и нечётные
        Map<Boolean, List<Integer>> evenOdd = numbers.stream()
            .collect(Collectors.partitioningBy(n -> n % 2 == 0));
        
        System.out.println("Even: " + evenOdd.get(true));   // [2, 4, 6]
        System.out.println("Odd: " + evenOdd.get(false));   // [1, 3, 5]
    }
    
    @Test
    public void partitioningWithDownstream() {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);
        
        // Разбиение с подсчётом
        Map<Boolean, Long> evenOddCount = numbers.stream()
            .collect(Collectors.partitioningBy(
                n -> n % 2 == 0,
                Collectors.counting()
            ));
        
        System.out.println("Even count: " + evenOddCount.get(true));   // 3
        System.out.println("Odd count: " + evenOddCount.get(false));   // 3
    }
}

Joining (соединение строк)

public class JoiningExample {
    
    @Test
    public void joiningStrings() {
        List<String> words = Arrays.asList("Hello", "World", "Java");
        
        // Соединение с разделителем
        String result = words.stream()
            .collect(Collectors.joining(", "));
        
        System.out.println(result); // Hello, World, Java
    }
    
    @Test
    public void joiningWithPrefixSuffix() {
        List<String> words = Arrays.asList("Hello", "World");
        
        String result = words.stream()
            .collect(Collectors.joining(", ", "[", "]"));
        
        System.out.println(result); // [Hello, World]
    }
}

Практическое сравнение

public class PracticalComparison {
    
    List<String> names = Arrays.asList(
        "Alice", "Bob", "Charlie", "David", "Eve"
    );
    
    // ✅ Когда нужен порядок и модификация -> List
    List<String> toList = names.stream()
        .filter(s -> s.length() > 3)
        .collect(Collectors.toList());
    
    // ✅ Когда нужны уникальные значения -> Set
    Set<String> toSet = names.stream()
        .map(String::toUpperCase)
        .collect(Collectors.toSet());
    
    // ✅ Когда нужна пара ключ-значение -> Map
    Map<String, Integer> toMap = names.stream()
        .collect(Collectors.toMap(
            s -> s,
            String::length
        ));
    
    // ✅ Когда нужно перенести в специфичный тип
    TreeSet<String> toTreeSet = names.stream()
        .collect(Collectors.toCollection(TreeSet::new));
    
    // ✅ Когда нужно разбить данные
    Map<Boolean, List<String>> partition = names.stream()
        .collect(Collectors.partitioningBy(s -> s.length() > 3));
    
    // ✅ Когда нужно сгруппировать
    Map<Integer, List<String>> grouped = names.stream()
        .collect(Collectors.groupingBy(String::length));
}

Частые ошибки

public class CommonMistakes {
    
    // ❌ Ошибка 1: забыли Collectors
    // List<Integer> list = stream.collect(toList());
    // ✅ Правильно:
    List<Integer> list = stream.collect(Collectors.toList());
    
    // ❌ Ошибка 2: используют collect без потока
    // Collectors.toList(); // это не работает, нужен stream.collect()
    
    // ❌ Ошибка 3: забыли merge function в toMap с дубликатами ключей
    // List<String> dup = Arrays.asList("a", "a");
    // dup.stream().collect(Collectors.toMap(s -> s, String::length));
    // Exception! IllegalStateException: Duplicate key
    
    // ✅ Правильно:
    Map<String, Integer> map = dup.stream()
        .collect(Collectors.toMap(
            s -> s,
            String::length,
            (a, b) -> a // merge function: берём первое значение
        ));
}

Производительность

public class PerformanceConsiderations {
    
    // ✅ Быстро: использование toList()
    List<Integer> list = numbers.stream()
        .filter(n -> n > 5)
        .collect(Collectors.toList()); // O(n)
    
    // ⚠️ Медленнее для больших данных: groupingBy
    Map<Integer, List<Integer>> grouped = numbers.stream()
        .collect(Collectors.groupingBy(n -> n % 10)); // O(n log n)
    
    // ✅ Параллельный сбор (для больших объёмов данных)
    List<Integer> parallel = numbers.parallelStream()
        .filter(n -> n > 5)
        .collect(Collectors.toList()); // multi-threaded
}

Заключение

Самые частые:

  • Collectors.toList() — собрание в список
  • Collectors.toSet() — собрание в множество
  • Collectors.toMap() — собрание в таблицу
  • Collectors.groupingBy() — группировка
  • Collectors.joining() — соединение строк

Мастерство работы с Collectors — важный навык для Java разработчика, так как Stream API везде.

В какие коллекции собирал Stream | PrepBro