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

Что такое Lazy Collection?

2.2 Middle🔥 201 комментариев
#Язык Swift

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

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

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

Что такое Lazy Collection?

Lazy Collection — это механизм в Swift для отложенного (ленивого) вычисления элементов коллекции. Вместо того, чтобы сразу вычислить все результаты, операции выполняются только когда они действительно нужны.

Основной концепт

Обычная коллекция выполняет операции немедленно:

let numbers = [1, 2, 3, 4, 5]

// Эта операция создаёт НОВЫЙ массив с результатами
let squared = numbers.map { $0 * $0 }
// Результат: [1, 4, 9, 16, 25]
// Все элементы вычислены, память выделена

LazySequence откладывает вычисления:

let numbers = [1, 2, 3, 4, 5]

// Это НЕ создаёт новый массив!
let lazySquared = numbers.lazy.map { $0 * $0 }
// Вычисления отложены, память не выделена

// Вычисления происходят ТОЛЬКО здесь при итерации
for squared in lazySquared {
    print(squared)  // 1, 4, 9, 16, 25
}

Практический пример

let largeArray = Array(1...1_000_000)

// ❌ Неэффективно: создаёт промежуточные массивы
let eager = largeArray
    .map { $0 * 2 }      // создаёт массив из 1М элементов
    .filter { $0 > 1000 } // создаёт ещё один массив
    .prefix(10)          // берёт только первые 10!
// Невыгодно: 2 миллиона элементов обработано, но нужно 10

// ✅ Эффективно: ленивые вычисления
let lazy = largeArray
    .lazy
    .map { $0 * 2 }      // ещё НЕ вычислено
    .filter { $0 > 1000 } // ещё НЕ вычислено
    .prefix(10)          // ТОЛЬКО здесь вычисляются нужные элементы

for value in lazy {
    print(value)  // обработано только нужное количество элементов
}

Методы, работающие с Lazy

Методы, которые работают с lazy (и остаются lazy)

let numbers = [1, 2, 3, 4, 5].lazy

// Остаются ленивыми (не вычисляют сразу):
let mapped = numbers.map { $0 * 2 }           // LazyMapSequence
let filtered = numbers.filter { $0 > 2 }      // LazyFilterSequence
let flattened = numbers.flatMap { 1...$0 }    // LazyFlatMapSequence
let zipped = numbers.zip(numbers)             // LazyZipSequence

Методы, которые "прерывают" lazy

let numbers = [1, 2, 3, 4, 5].lazy

// Эти операции ВЫЧИСЛЯЮТ все значения сразу:
let array = Array(numbers)              // Создаёт массив
let count = numbers.count               // Подсчитывает всё
let first = numbers.first               // Нужно проверить всё
let contains = numbers.contains(3)      // Может нужно всё

Таблица: Eager vs Lazy

ОперацияEagerLazy
Когда выполняетсяСразуПри итерации
ПамятьСоздаёт промежуточные массивыМинимальная
СкоростьМедленнее для больших данныхБыстрее для коротких результатов
ТипArrayLazySequence
Примерarray.map { }array.lazy.map { }

Реальные примеры использования

1. Большие файлы

// Обработка большого файла строка за строкой
func processLargeFile(_ path: String) {
    let lines = readLines(path)  // читает весь файл
    
    lines
        .lazy
        .map { $0.trimmingCharacters(in: .whitespaces) }  // ленивое трим
        .filter { !$0.isEmpty }                           // ленивый фильтр
        .forEach { processLine($0) }                      // обрабатывает по одной
}

2. Бесконечные последовательности

func fibonacci() -> LazySequence<UnfoldSequence<Int, (Int, Int)>> {
    return sequence(state: (0, 1)) { state in
        let next = state.0 + state.1
        state = (state.1, next)
        return next
    }.lazy
}

// Берём первые 10 чисел Фибоначчи
let first10 = fibonacci().prefix(10)
for num in first10 {
    print(num)  // вычисляются только нужные
}

3. API запросы

func fetchUsersPaginated(pageSize: Int) -> LazySequence<LazyMapSequence<[Int], User>> {
    return (1...).lazy.map { page in
        // Каждый page загружается ТОЛЬКО когда на него обращаются
        fetchPage(page: page, size: pageSize)
    }.flatMap { $0 }
}

// Используем
for user in fetchUsersPaginated(pageSize: 20).prefix(100) {
    print(user)  // загружаются только нужные страницы
}

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

Тест на производительность

import Foundation

let numbers = Array(1...10_000)

// Eager
let startEager = Date()
let eagerResult = numbers
    .map { $0 * 2 }
    .filter { $0 > 5000 }
    .prefix(5)
let timeEager = Date().timeIntervalSince(startEager)

// Lazy
let startLazy = Date()
let lazyResult = numbers
    .lazy
    .map { $0 * 2 }
    .filter { $0 > 5000 }
    .prefix(5)
    .forEach { _ in } // материализуем
let timeLazy = Date().timeIntervalSince(startLazy)

print("Eager: \(timeEager)s")  // медленнее
print("Lazy: \(timeLazy)s")    // быстрее

Когда использовать Lazy

✅ Используй Lazy когда:

  • Работаешь с большими коллекциями
  • Нужны первые N элементов, а не все
  • Данные дорого вычислять (API запросы, файлы)
  • Хочешь цепочку трансформаций без промежуточных массивов
  • Имеешь бесконечные последовательности

❌ Не используй Lazy когда:

  • Коллекция маленькая (< 100 элементов)
  • Нужны все элементы (всё равно будут вычислены)
  • Нужна случайный доступ по индексу
  • Нужна производительность для одноразовой итерации

Вывод

Lazy Collection — это мощная оптимизация для работы с большими данными и сложными трансформациями. Она откладывает вычисления до момента, когда они действительно нужны, экономя память и время обработки. Это особенно полезно при работе с файлами, API запросами и бесконечными последовательностями.

Что такое Lazy Collection? | PrepBro