Dart: Coleções no Dart
Última atualização: 2026-08-26
Coleções são o exército dos dados — listas ordenadas, conjuntos deduplicados, mapas indexados. Combine-os e você se torna invencível.
1. O que Você Aprenderá
- List: Criar / Percorrer / Ordenar / Spread (
...) e Elementos Collection-if/for - Set: Deduplicação e Operações de Conjunto (Interseção / União / Diferença)
- Map: Operações com Chave-Valor e Iteração
- Inferência de Tipos em Coleções e Coleções Const
- Cenário do Bob: Agrupamento e Agregação de Milhões de Pedidos
2. A História Real de um Desenvolvedor
(1) A Dor: Agrupamento e Agregação Manual é Demorado e Propenso a Erros
Bob precisa agrupar 1.200.000 pedidos por categoria, calculando a receita total e a contagem de pedidos para cada um. Ele implementou manualmente a lógica de agrupamento com um loop for, escrevendo 50 linhas de código, mas encontrou problemas como sobrescrita de chaves duplicadas e ordenação instável. Processar 1.200.000 registros levou 8 segundos.
(2) A Solução: Operações com Coleções
As operações com coleções do Dart combinadas com funções de alta ordem reduzem o agrupamento e a agregação a apenas 10 linhas de código, com uma melhoria de 40% na velocidade.
// Agrupar pedidos por categoria e agregar
final byCategory = <String, List<Order>>{};
for (final order in orders) {
byCategory.putIfAbsent(order.category, () => []).add(order);
}
// Ou usar fold para agregação
final revenueByCategory = orders.fold<Map<String, double>>(
{}, (acc, o) => acc..update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount));
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
(3) Os Benefícios
- 80% de redução no volume de código, com lógica mais clara
- A deduplicação do Set evita dados duplicados, o agrupamento com Map substitui a indexação manual
- Spread e collection-if tornam a construção de coleções mais declarativa
3. List
(1) Criação e Operações Básicas
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Criação de List
void main() {
// Criação literal
List<int> counts = [1, 2, 3, 4, 5];
var amounts = <double>[1500.0, 3200.0, 890.0];
// Lista expansível
var orders = <String>['ORD-001', 'ORD-002'];
orders.add('ORD-003');
// Lista de comprimento fixo
var buffer = List<double>.filled(5, 0.0);
buffer[0] = 100.0;
// Gerar lista
var batch = List.generate(10, (i) => 'Batch-${i + 1}');
print(counts); // [1, 2, 3, 4, 5]
print(orders); // [ORD-001, ORD-002, ORD-003]
print(buffer); // [100.0, 0.0, 0.0, 0.0, 0.0]
print(batch); // [Batch-1, ..., Batch-10]
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Percurso e Ordenação de List
void main() {
var amounts = [1500.0, 890.0, 3200.0, 50.0];
// Ordenar in-place
amounts.sort();
print(amounts); // [50.0, 890.0, 1500.0, 3200.0]
// Ordenar de forma decrescente (criar nova lista)
var descending = [...amounts]..sort((a, b) => b.compareTo(a));
print(descending); // [3200.0, 1500.0, 890.0, 50.0]
// Acessar elementos
print(amounts.first); // 50.0
print(amounts.last); // 3200.0
print(amounts[2]); // 1500.0
// Sublista
print(amounts.sublist(1, 3)); // [890.0, 1500.0]
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Spread e Collection-if/for
void main() {
var base = ['ORD-001', 'ORD-002'];
var extra = ['ORD-003', 'ORD-004'];
// Operador spread
var combined = [...base, ...extra];
print(combined); // [ORD-001, ORD-002, ORD-003, ORD-004]
// Spread com consciência de null
List<String>? maybeList;
var safe = ['header', ...?maybeList, 'footer'];
print(safe); // [header, footer]
// Collection-if
bool includePremium = true;
var tiers = [
'Standard',
if (includePremium) 'Premium',
'Enterprise',
];
print(tiers); // [Standard, Premium, Enterprise]
// Collection-for
var batch = [
for (int i = 1; i <= 3; i++) 'Batch-$i',
];
print(batch); // [Batch-1, Batch-2, Batch-3]
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Operação | Sintaxe | Descrição |
|---|---|---|
| spread | ...list |
Espalha os elementos |
| spread com consciência de null | ...?list |
Espalha uma lista anulável |
| collection-if | if (cond) expr |
Inclusão condicional |
| collection-for | for (var x in list) expr |
Geração em loop |
4. Set
(1) Criação e Operações de Conjunto
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Deduplicação e Operações com Set
void main() {
// Criar a partir de lista (remove duplicatas)
var allOrders = ['ORD-001', 'ORD-002', 'ORD-001', 'ORD-003'];
var uniqueOrders = <String>{...allOrders};
print(uniqueOrders); // {ORD-001, ORD-002, ORD-003}
// Operações de conjunto
var setA = {1, 2, 3, 4, 5};
var setB = {4, 5, 6, 7, 8};
print(setA.intersection(setB)); // {4, 5} - Interseção
print(setA.union(setB)); // {1, 2, 3, 4, 5, 6, 7, 8} - União
print(setA.difference(setB)); // {1, 2, 3} - Diferença
// Teste de pertencimento
print(setA.contains(3)); // true
print(setA.containsAll({1, 2})); // true
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Aplicação de Set em Cenários de Deduplicação
void main() {
// Deduplicar categorias a partir da lista de pedidos
var orders = [
{'id': 'ORD-001', 'category': 'Electronics'},
{'id': 'ORD-002', 'category': 'Books'},
{'id': 'ORD-003', 'category': 'Electronics'},
{'id': 'ORD-004', 'category': 'Clothing'},
{'id': 'ORD-005', 'category': 'Books'},
];
var categories = orders.map((o) => o['category'] as String).toSet();
print(categories); // {Electronics, Books, Clothing}
print('Unique categories: ${categories.length}'); // 3
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Operação | Método | Resultado |
|---|---|---|
| Interseção | a.intersection(b) |
Elementos comuns |
| União | a.union(b) |
Todos os elementos |
| Diferença | a.difference(b) |
Elementos em a que não estão em b |
| Contém | a.contains(e) |
bool |
| Contém Todos | a.containsAll(b) |
bool |
5. Map
(1) Criação e Operações
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Criação de Map e Operações Básicas
void main() {
// Criação literal
var config = <String, dynamic>{
'appName': 'DataPipeline',
'maxRecords': 1000000,
'taxRate': 0.08,
'verbose': true,
};
// Acessar valores
print(config['appName']); // DataPipeline
print(config['missing']); // null
print(config['missing'] ?? 'N/A'); // N/A
// Adicionar/atualizar entradas
config['outputPath'] = '/tmp/reports'; // Adicionar
config['taxRate'] = 0.10; // Atualizar
// Atualização segura
config.update('maxRecords', (v) => v * 2, ifAbsent: () => 500000);
// Iterar
config.forEach((key, value) => print(' $key: $value'));
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Iteração e Agregação com Map
void main() {
var orders = [
{'id': 'ORD-001', 'category': 'Electronics', 'amount': 1500.0},
{'id': 'ORD-002', 'category': 'Books', 'amount': 50.0},
{'id': 'ORD-003', 'category': 'Electronics', 'amount': 3200.0},
{'id': 'ORD-004', 'category': 'Clothing', 'amount': 890.0},
{'id': 'ORD-005', 'category': 'Books', 'amount': 120.0},
];
// Agrupar por categoria
final byCategory = <String, List<Map<String, dynamic>>>{};
for (final order in orders) {
final cat = order['category'] as String;
byCategory.putIfAbsent(cat, () => []).add(order);
}
// Agregar receita por categoria
final revenue = <String, double>{};
for (final entry in byCategory.entries) {
final total = entry.value.fold<double>(
0, (sum, o) => sum + (o['amount'] as double));
revenue[entry.key] = total;
}
// Imprimir resultados
revenue.forEach((cat, total) =>
print('$cat: \$${total.toStringAsFixed(2)} USD'));
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
6. Inferência de Tipos em Coleções e Coleções Const
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Inferência de Tipos e Coleções Const
void main() {
// Inferência de tipos
var list1 = [1, 2, 3]; // List<int>
var list2 = [1, 2, 3.0]; // List<num> (mistura de int e double)
var list3 = <String>[]; // Genérico explícito para lista vazia
// Coleções const - profundamente imutáveis
const categories = ['Electronics', 'Books', 'Clothing'];
const config = <String, int>{'batchSize': 10000, 'timeout': 30};
const flags = {true, false};
// categories.add('Sports'); // Erro! Não é possível modificar const
// Const em classe
const defaultFormats = ['json', 'csv', 'html'];
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
| Recurso | var | final | const |
|---|---|---|---|
| Conteúdo Mutável | Sim | Sim (referência imutável) | Não |
| Inferência de Tipo | Automática | Automática | Automática |
| Constante em Tempo de Compilação | Não | Não | Sim |
7. Cenário do Bob: Agrupamento e Agregação de Milhões de Pedidos
flowchart LR A[List: 1.2M Pedidos] --> B[where: Filtrar] B --> C[groupBy: Categoria] C --> D[Map: Categoria → Pedidos] D --> E[fold: Agregar] E --> F[Saída: Estatísticas]
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
▶ Exemplo
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
: Agrupamento e Agregação na Prática
class Order {
final String id;
final String category;
final double amount;
final String status;
Order({required this.id, required this.category, required this.amount, required this.status});
}
Map<String, Map<String, dynamic>> analyzeOrders(List<Order> orders) {
// Filtrar pedidos concluídos
final completed = orders.where((o) => o.status == 'completed').toList();
// Agrupar por categoria
final grouped = <String, List<Order>>{};
for (final order in completed) {
grouped.putIfAbsent(order.category, () => []).add(order);
}
// Agregar por categoria
final result = <String, Map<String, dynamic>>{};
for (final entry in grouped.entries) {
final total = entry.value.fold<double>(0, (s, o) => s + o.amount);
final avg = total / entry.value.length;
result[entry.key] = {
'count': entry.value.length,
'total': total,
'average': avg,
};
}
return result;
}
void main() {
final orders = [
Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed'),
Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed'),
Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending'),
Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed'),
Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed'),
];
final analysis = analyzeOrders(orders);
print('=== Análise por Categoria ===');
for (final entry in analysis.entries) {
final stats = entry.value;
print('${entry.key}:');
print(' Pedidos: ${stats['count']}');
print(' Total: \$${(stats['total'] as double).toStringAsFixed(2)} USD');
print(' Média: \$${(stats['average'] as double).toStringAsFixed(2)} USD');
}
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
8. Exemplo Completo: Analisador de Coleções do DataPipeline
// ============================================
// Analisador de Coleções do DataPipeline
// Demonstração completa de operações com List, Set, Map
// ============================================
class Order {
final String id;
final String category;
final double amount;
final String status;
final String region;
const Order({
required this.id,
required this.category,
required this.amount,
required this.status,
this.region = 'US',
});
}
class OrderAnalyzer {
final List<Order> orders;
OrderAnalyzer(this.orders);
// Categorias únicas (Set)
Set<String> get categories => orders.map((o) => o.category).toSet();
// Regiões únicas (Set)
Set<String> get regions => orders.map((o) => o.region).toSet();
// Pedidos concluídos (filtro List)
List<Order> get completed =>
orders.where((o) => o.status == 'completed').toList();
// Receita total (fold)
double get totalRevenue =>
completed.fold(0.0, (sum, o) => sum + o.amount);
// Receita por categoria (agrupamento Map)
Map<String, double> get revenueByCategory {
return completed.fold<Map<String, double>>({}, (acc, o) {
acc.update(o.category, (v) => v + o.amount, ifAbsent: () => o.amount);
return acc;
});
}
// Contagem de pedidos por região e status
Map<String, Map<String, int>> get countByRegionStatus {
final result = <String, Map<String, int>>{};
for (final order in orders) {
result.putIfAbsent(order.region, () => {});
result[order.region]!.update(
order.status, (v) => v + 1, ifAbsent: () => 1);
}
return result;
}
// Top N categorias por receita
List<MapEntry<String, double>> topCategories(int n) {
final sorted = revenueByCategory.entries.toList()
..sort((a, b) => b.value.compareTo(a.value));
return sorted.take(n).toList();
}
void printReport() {
print('=== Relatório de Analytics do DataPipeline ===');
print('Total de pedidos: ${orders.length}');
print('Concluídos: ${completed.length}');
print('Categorias únicas: ${categories.length}');
print('Regiões: ${regions}');
print('Receita total: \$${totalRevenue.toStringAsFixed(2)} USD');
print('\n--- Receita por Categoria ---');
for (final entry in revenueByCategory.entries) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
print('\n--- Top Categorias ---');
for (final entry in topCategories(3)) {
print(' ${entry.key}: \$${entry.value.toStringAsFixed(2)} USD');
}
print('\n--- Pedidos por Região e Status ---');
for (final region in countByRegionStatus.entries) {
print(' ${region.key}: ${region.value}');
}
}
}
void main() {
final orders = [
Order(id: 'ORD-001', category: 'Electronics', amount: 1500.0, status: 'completed', region: 'US'),
Order(id: 'ORD-002', category: 'Books', amount: 50.0, status: 'completed', region: 'EU'),
Order(id: 'ORD-003', category: 'Electronics', amount: 3200.0, status: 'pending', region: 'US'),
Order(id: 'ORD-004', category: 'Clothing', amount: 890.0, status: 'completed', region: 'EU'),
Order(id: 'ORD-005', category: 'Books', amount: 120.0, status: 'completed', region: 'US'),
Order(id: 'ORD-006', category: 'Electronics', amount: 2100.0, status: 'completed', region: 'AP'),
];
OrderAnalyzer(orders).printReport();
}
> **Saída:** Execute localmente no DartPad ou via `dart run`. Todos os exemplos do curso Dart são baseados no Dart 3.x / Flutter 3.x. Os resultados podem variar ligeiramente dependendo da versão do SDK.
Saída:
=== Relatório de Analytics do DataPipeline ===
Total de pedidos: 6
Concluídos: 5
Categorias únicas: 3
Regiões: {US, EU, AP}
Receita total: $4660.00 USD
--- Receita por Categoria ---
Electronics: $3600.00 USD
Clothing: $890.00 USD
Books: $170.00 USD
--- Top Categorias ---
Electronics: $3600.00 USD
Clothing: $890.00 USD
Books: $170.00 USD
--- Pedidos por Região e Status ---
US: {completed: 2, pending: 1}
EU: {completed: 2}
AP: {completed: 1}
❓ Perguntas Frequentes
P: Qual é a diferença de desempenho entre List e Set? R: List armazena elementos na ordem de inserção, com acesso por índice O(1) e busca O(n). Set é baseado em hash, oferecendo busca/verificação de pertencimento O(1), mas sem acesso por índice. Use Set para cenários de deduplicação e List quando a ordem importa.
P: As chaves de um Map podem ser de qualquer tipo? R: Sim, mas ao usar classes personalizadas como chaves, você deve sobrescrever
==ehashCode; caso contrário, o Map não conseguirá encontrar as entradas corretamente. É recomendado usar String ou int como chaves.
P: Devo usar
[]ou<Type>[]para uma List vazia? R: Use[]quando o contexto de inferência de tipo estiver disponível. Use<Type>[]ao declarar independentemente para evitar a inferência comoList<dynamic>.
P: Collection-if e collection-for só podem ser usados em literais de List? R: Não, literais de List, Set e Map todos suportam collection-if e collection-for. Por exemplo:
{for (var i in items) i.name: i.value}.
P: Qual é a diferença entre
putIfAbsenteupdatedo Map? R:putIfAbsentsó define o valor se a chave não existir.updatesó atualiza o valor se a chave existir (use o parâmetroifAbsentpara lidar com casos de chave inexistente).
P: Qual é a diferença entre coleções const e coleções final? R: Uma coleção const é uma constante em tempo de compilação, com conteúdo inteiramente imutável (imutabilidade profunda). Uma coleção final tem uma referência imutável, mas o conteúdo pode ser mutável (ex: uma List final pode ter elementos adicionados).
P: Como posso ordenar uma List grande de forma eficiente? R:
List.sort()ordena in-place, o que é mais eficiente do que[...list].sort()(que cria uma cópia). Se você não quiser modificar a lista original, crie uma cópia primeiro com[...list]e depois ordene.
📖 Resumo
- List é ordenada e permite duplicatas, suportando spread (
...) e collection-if/for para construção declarativa. - Set é não ordenado e impede duplicatas, com busca O(1), suportando operações de interseção/união/diferença.
- Map armazena pares chave-valor;
putIfAbsenteupdatesão ferramentas poderosas para agrupamento e agregação. - Coleções const são profundamente imutáveis; coleções final têm referências imutáveis, mas conteúdo mutável.
- Funções de alta ordem combinadas com operações de coleção podem reduzir o processamento de dados de 50 linhas para apenas 10.
📝 Exercícios
- Básico (Dificuldade ⭐): Crie uma List contendo 10 valores de pedidos. Use
wherepara filtrar valores maiores que 500,mappara formatá-los em USD, ereducepara calcular a soma total. - Intermediário (Dificuldade ⭐⭐): Dado um conjunto de dados de pedidos, use
putIfAbsentefolddo Map para agrupar pedidos por categoria, calculando a contagem de pedidos e a receita total para cada categoria, e gere um relatório formatado. - Desafio (Dificuldade ⭐⭐⭐): Implemente uma classe
CollectionPipelineque suporte encadeamento de operações comowhere,map,sortBy,groupByeaggregate, gerando os resultados da análise. Dica: Cada etapa retorna uma nova coleção ou Map.