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

Что такое файл .pyc?

2.0 Middle🔥 141 комментариев
#DevOps и инфраструктура#Django

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

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

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

Stream.collect в HashMap

Да, я часто использовал Stream API для сборки данных в HashMap используя Collectors. Это очень мощный и элегантный способ трансформации данных в Java.

Базовый пример

List<User> users = Arrays.asList(
    new User(1L, "John", "john@example.com"),
    new User(2L, "Jane", "jane@example.com"),
    new User(3L, "Bob", "bob@example.com")
);

// Собрать Stream в HashMap по id
Map<Long, User> usersById = users.stream()
    .collect(Collectors.toMap(
        User::getId,           // key: id
        Function.identity()    // value: сам объект User
    ));

// Результат: {1=User(John), 2=User(Jane), 3=User(Bob)}

Использование с трансформацией значений

// Собрать HashMap <id, email>
Map<Long, String> userEmails = users.stream()
    .collect(Collectors.toMap(
        User::getId,      // key: id
        User::getEmail    // value: email
    ));

// Результат: {1=john@example.com, 2=jane@example.com, 3=bob@example.com}

Обработка дублей (merge function)

Если может быть несколько объектов с одинаковым key, нужно указать как их мёржить:

List<Order> orders = Arrays.asList(
    new Order(1L, "John", 100),
    new Order(2L, "John", 200),  // Дубль по пользователю!
    new Order(3L, "Jane", 150)
);

// НЕПРАВИЛЬНО: Выбросит exception "Duplicate key"
// Map<String, Order> byUser = orders.stream()
//    .collect(Collectors.toMap(Order::getUserName, Function.identity()));

// ПРАВИЛЬНО: Указать merge function
Map<String, Integer> totalByUser = orders.stream()
    .collect(Collectors.toMap(
        Order::getUserName,
        Order::getAmount,
        Integer::sum         // Если дубль, сложить суммы
    ));

// Результат: {John=300, Jane=150}

Более сложные примеры

1. HashMap <Category, List of Products>

List<Product> products = Arrays.asList(
    new Product("Laptop", "Electronics", 1000),
    new Product("Mouse", "Electronics", 50),
    new Product("Desk", "Furniture", 300),
    new Product("Chair", "Furniture", 200)
);

// Группировать по категориям
Map<String, List<Product>> byCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::getCategory
    ));

// Результат:
// {
//   Electronics=[Laptop, Mouse],
//   Furniture=[Desk, Chair]
// }

2. HashMap <Category, Total Price>

// Группировать И считать сумму
Map<String, Integer> totalByCategory = products.stream()
    .collect(Collectors.groupingBy(
        Product::getCategory,
        Collectors.summingInt(Product::getPrice)
    ));

// Результат: {Electronics=1050, Furniture=500}

3. HashMap с HashMap inside (nested)

List<Order> orders = Arrays.asList(
    new Order(1L, "John", "Electronics", 100),
    new Order(2L, "John", "Furniture", 200),
    new Order(3L, "Jane", "Electronics", 150)
);

// Группировать по пользователю, потом по категории
Map<String, Map<String, List<Order>>> byUserAndCategory =
    orders.stream()
    .collect(Collectors.groupingBy(
        Order::getUserName,
        Collectors.groupingBy(Order::getCategory)
    ));

// Результат:
// {
//   John: {
//     Electronics: [Order 1],
//     Furniture: [Order 2]
//   },
//   Jane: {
//     Electronics: [Order 3]
//   }
// }

Real-world примеры

1. Кэш пользователей по email

@Service
public class UserService {
    public Map<String, User> getUsersByEmail(List<Long> userIds) {
        return userRepository.findAllById(userIds)
            .stream()
            .collect(Collectors.toMap(
                User::getEmail,
                Function.identity()
            ));
    }
}

// Использование
Map<String, User> cache = userService.getUsersByEmail(Arrays.asList(1L, 2L, 3L));
User user = cache.get("john@example.com");

2. Валидация уникальных значений

public boolean validateEmails(List<String> emails) {
    Map<String, Long> emailCounts = emails.stream()
        .collect(Collectors.groupingBy(
            Function.identity(),
            Collectors.counting()
        ));
    
    // Проверить что нет дублей
    return emailCounts.values().stream()
        .allMatch(count -> count == 1);
}

3. Статистика по заказам

List<Order> orders = repository.findAll();

Map<String, OrderStats> stats = orders.stream()
    .collect(Collectors.groupingBy(
        Order::getStatus,
        Collectors.collectingAndThen(
            Collectors.toList(),
            list -> new OrderStats(
                list.size(),                     // count
                list.stream().mapToLong(o -> o.getAmount()).sum(),  // total
                list.stream().mapToLong(o -> o.getAmount()).average().orElse(0)  // avg
            )
        )
    ));

// Результат:
// {
//   PENDING: OrderStats(count=10, total=1500, avg=150),
//   COMPLETED: OrderStats(count=50, total=7500, avg=150),
//   CANCELLED: OrderStats(count=5, total=300, avg=60)
// }

Partition (разделение на true/false)

List<User> users = repository.findAll();

// Разделить на активных и неактивных
Map<Boolean, List<User>> partitioned = users.stream()
    .collect(Collectors.partitioningBy(User::isActive));

List<User> activeUsers = partitioned.get(true);
List<User> inactiveUsers = partitioned.get(false);

Custom collector

Когда стандартных collectors не хватает:

public class CustomCollectors {
    
    // Собрать в HashMap с custom merge logic
    public static <T, K, V> Collector<T, ?, Map<K, V>> toMapWithMerge(
            Function<? super T, ? extends K> keyMapper,
            Function<? super T, ? extends V> valueMapper,
            BiFunction<? super V, ? super V, ? extends V> mergeFunction) {
        return Collectors.toMap(keyMapper, valueMapper, mergeFunction);
    }
    
    // Использование
    Map<Long, User> merged = users.stream()
        .collect(CustomCollectors.toMapWithMerge(
            User::getId,
            Function.identity(),
            (existing, incoming) -> incoming  // Перезаписать старое на новое
        ));
}

Проблемы которые я решал

Проблема 1: Null values в HashMap

// НЕПРАВИЛЬНО: NullPointerException если User.getEmail() return null
Map<String, User> map = users.stream()
    .collect(Collectors.toMap(User::getEmail, Function.identity()));

// ПРАВИЛЬНО: Филтровать null значения
Map<String, User> map = users.stream()
    .filter(u -> u.getEmail() != null)
    .collect(Collectors.toMap(User::getEmail, Function.identity()));

// ИЛИ: Использовать nullsLast для sorting
List<User> sorted = users.stream()
    .sorted(Comparator.comparing(
        User::getEmail,
        Comparator.nullsLast(String::compareTo)
    ))
    .collect(Collectors.toList());

Проблема 2: Concurrent modification при работе с HashMap

// Если нужна thread-safe Map
Map<Long, User> concurrentMap = users.stream()
    .collect(Collectors.toMap(
        User::getId,
        Function.identity(),
        (a, b) -> a,
        ConcurrentHashMap::new  // Использовать ConcurrentHashMap
    ));

Проблема 3: Ленивое выполнение (Lazy evaluation)

// Stream ленивый! Это не выполнится пока не вызовешь terminal operation
Stream<User> stream = users.stream()
    .filter(u -> u.isActive())
    .map(User::getName);

// Collect — это terminal operation, который выполнит весь stream
Map<Long, String> result = users.stream()
    .filter(u -> u.isActive())
    .collect(Collectors.toMap(User::getId, User::getName));

Performance tips

1. Избегай multiple iterations

// ПЛОХО: Два отдельных stream'а
List<String> names = users.stream()
    .map(User::getName)
    .collect(Collectors.toList());

Map<String, User> byName = users.stream()
    .collect(Collectors.toMap(User::getName, Function.identity()));

// ХОРОШО: Один stream с несколькими collect
AbstractMap.SimpleEntry<List<String>, Map<String, User>> result =
    new AbstractMap.SimpleEntry<>(
        users.stream().map(User::getName).collect(Collectors.toList()),
        users.stream().collect(Collectors.toMap(User::getName, Function.identity()))
    );

// ИЛИ используй custom collector

2. Используй parallelStream для больших данных

// Для 100k+ items параллелизм может ускорить
Map<Long, User> map = users.parallelStream()  // Parallel!
    .collect(Collectors.toMap(
        User::getId,
        Function.identity(),
        (a, b) -> a,
        ConcurrentHashMap::new  // MUST be concurrent!
    ));

Чек-лист при использовании toMap

  1. Может ли быть null в key? — Используй filter
  2. Может ли быть дубль в key? — Указать merge function
  3. Нужна ли thread-safety? — Использовать ConcurrentHashMap
  4. Большой объём данных? — Попробовать parallelStream
  5. Нужно хранить оригинальный объект или часть? — Выбрать value mapper

Заключение

Stream.collect(Collectors.toMap(...)) — это очень мощный и элегантный способ трансформации данных в Java. За 10 лет я использовал это в сотнях мест:

  • Создание кэшей
  • Группировка данных
  • Трансформация коллекций
  • Статистика и аналитика
  • Валидация данных

Это один из моих фаворитов в Java 8+ Stream API.

Что такое файл .pyc? | PrepBro