Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между Stream и Iterable
Stream и Iterable — два фундаментальных типа в Dart для работы с последовательностями данных. Хотя оба представляют коллекции элементов, они различаются способом получения данных и использованием.
Основные отличия
Iterable
- Синхронна — данные доступны немедленно
- Лениво вычисляется — элементы генерируются по запросу
- Неподдерживает асинхронные данные — все элементы существуют в памяти или могут быть синхронно вычислены
- Многократно используется — можно обходить несколько раз
- Примеры: List, Set, Map.entries
Iterable<int> numbers = [1, 2, 3, 4, 5];
for (var n in numbers) {
print(n); // Доступно сразу
}
Stream
- Асинхронна — данные приходят со временем
- Реактивна — активно отправляет данные слушателям
- Поддерживает асинхронные источники — может получать данные из API, базы данных, пользовательского взаимодействия
- Одноразовое использование — каждый элемент обрабатывается один раз (кроме broadcast streams)
- Примеры: Future, WebSocket, сенсоры телефона
Stream<int> numbers = Stream.fromIterable([1, 2, 3, 4, 5]);
numbers.listen((n) {
print(n); // Данные приходят асинхронно
});
Синхронность vs Асинхронность
Iterable — синхронная
// Все данные доступны в момент обращения
List<int> numbers = [1, 2, 3, 4, 5];
print(numbers[0]); // Мгновенно выдаст 1
Iterable<int> evens = numbers.where((n) => n.isEven);
for (var e in evens) {
print(e); // 2, затем 4
}
Stream — асинхронная
// Данные приходят со временем
final stream = Stream.periodic(Duration(seconds: 1), (count) => count);
stream.listen((value) {
print('Получено: $value'); // Получит 0, потом 1, потом 2...
});
// Главный поток продолжает работу, не ждёт Stream
print('После подписки'); // Напечатается сразу же
Практические примеры
Iterable в действии
void iterableExample() {
// Создание Iterable
final numbers = [1, 2, 3, 4, 5];
// Трансформация (ленивая — не выполняется пока не обойдём)
final doubled = numbers.map((n) => n * 2);
print(doubled); // (2, 4, 6, 8, 10)
// Фильтрация
final evens = numbers.where((n) => n.isEven);
for (var e in evens) {
print(e); // 2, 4
}
// Комбинирование
final result = numbers
.where((n) => n > 2)
.map((n) => n * 2)
.toList(); // [6, 8, 10]
}
Stream в действии
future<void> streamExample() async {
// Создание Stream
final stream = Stream.periodic(
Duration(seconds: 1),
(count) => count,
);
// Подписка с listen
stream.listen(
(value) => print('Значение: $value'),
onError: (error) => print('Ошибка: $error'),
onDone: () => print('Stream завершён'),
);
// Трансформация Stream
stream
.where((n) => n.isEven)
.map((n) => n * 2)
.listen((n) => print('Чётное * 2: $n'));
}
Ленивость vs Нетерпеливость
Iterable — ленива (Lazy)
void lazyExample() {
final numbers = [1, 2, 3, 4, 5];
// Эта трансформация НЕ выполняется сразу
final transformed = numbers
.map((n) {
print('Трансформирую: $n');
return n * 2;
});
print('После map'); // Это напечатается раньше всех трансформаций!
// Только при обходе выполняются трансформации
print('Начинаю обход:');
for (var v in transformed) {
print('Результат: $v');
}
// Вывод:
// После map
// Начинаю обход:
// Трансформирую: 1
// Результат: 2
// Трансформирую: 2
// Результат: 4
// ...
}
Stream — также ленива
void streamLazy() {
final stream = Stream.fromIterable([1, 2, 3]);
// Stream НЕ начинает работать пока нет слушателей
stream.map((n) => n * 2);
// Только при listen() начинается работа
stream.listen((n) => print(n));
}
Многократность использования
Iterable — можно переиспользовать
final numbers = [1, 2, 3];
// Первый обход
for (var n in numbers) print(n); // 1, 2, 3
// Второй обход — работает!
for (var n in numbers) print(n); // 1, 2, 3
Stream — одноразовый (обычно)
final stream = Stream.fromIterable([1, 2, 3]);
stream.listen((n) => print(n)); // 1, 2, 3
stream.listen((n) => print(n)); // Ничего! Stream уже израсходован
// Для переиспользования нужен broadcast stream
final broadcastStream = stream.asBroadcastStream();
broadcastStream.listen((n) => print(n)); // Работает
broadcastStream.listen((n) => print(n)); // Работает
Таблица сравнения
Iterable Stream
─────────────────────────────────────────────
Синхронность Синхронна Асинхронна
Источник данных Статичен Динамичен
Множественное исп. Да Нет (обычно)
Ошибки Исключения onError callback
Завершение Конец коллекции onDone callback
Оперативность Нет Да (может быть)
Где используется Collections Реальное время
Обработка ошибок try/catch Stream.catchError()
Случаи использования
Используй Iterable для:
- Работы с коллекциями (List, Set, Map)
- Синхронной обработки данных
- Преобразования массивов (map, filter, reduce)
- Когда всё нужно сразу
// Получить все чётные числа из списка
final evens = numbers.where((n) => n.isEven).toList();
Используй Stream для:
- Реального времени обновлений
- Асинхронных источников данных
- WebSocket соединений
- Сенсоров и событий
- UI реактивности
// Получать обновления положения GPS в реальном времени
Geolocator.getPositionStream().listen((position) {
updateMapLocation(position);
});
Преобразование между ними
Iterable → Stream
final iterable = [1, 2, 3, 4, 5];
final stream = Stream.fromIterable(iterable);
Stream → Future (получить все значения)
final stream = Stream.fromIterable([1, 2, 3]);
final list = await stream.toList(); // [1, 2, 3]
Stream → Iterable (получить первый элемент)
final stream = Stream.periodic(Duration(seconds: 1), (i) => i);
final first = await stream.first; // Получит 0
Использование в Flutter
StreamBuilder для UI
StreamBuilder<String>(
stream: userRepository.getUserNameStream(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data!);
}
return CircularProgressIndicator();
},
)
Iterable.map для списков
ListView(
children: items
.map((item) => ListTile(title: Text(item.name)))
.toList(),
)
Вывод
- Iterable — для синхронной работы с коллекциями данных
- Stream — для асинхронной работы и потоков данных в реальном времени
Выбор между ними зависит от того, получаются ли данные синхронно (Iterable) или асинхронно (Stream). Оба инструмента критичны для современной разработки на Dart/Flutter.