Swift: Tutorial de Sets e Dicionários do Swift
Um Set é como um saco de bolinhas coloridas — uma de cada cor, misturadas sem ordem específica. Um Dicionário é como uma agenda telefônica — encontre um número rapidamente pelo nome. Esta lição aborda ambas as poderosas estruturas de dados.
1. O Que Você Vai Aprender
- Criar e manipular Sets, incluindo interseção, união e outras operações
- Criar e manipular Dicionários para armazenamento de chave-valor
- Entender o protocolo Hashable e tipos personalizados como chaves
- Usar Sets para desduplicação eficiente e verificações de pertinência
- Percorrer e modificar Dicionários com métodos comuns
2. Uma História Real de um Engenheiro Back-End
(1) Problema: 500 linhas de if-else para desduplicação de tags de usuário — dolorosamente lento
Bob está construindo um sistema de perfil de usuário. Cada usuário tem múltiplas tags (ex.: "VIP", "SensivelAPromocoes", "AltoGasto") e ele precisa de operações extensivas de conjuntos: encontrar usuários que são tanto VIP quanto de alto gasto, mesclar tags antigas e novas, excluir tags bloqueadas. Ele começou com arrays e desduplicação baseada em laços:
let oldTags = ["VIP", "HighSpender", "NewUser"]
let newTags = ["VIP", "PromoSensitive", "HighSpender"]
var merged: [String] = []
for tag in oldTags + newTags {
if !merged.contains(tag) {
merged.append(tag)
}
}
200.000 usuários x algoritmo O(n^2) = CPU do servidor no máximo por 20 minutos. Bob recebeu reclamações da equipe de operações.
(2) A Solução com Set e Dicionário
let oldTags: Set = ["VIP", "HighSpender", "NewUser"]
let newTags: Set = ["VIP", "PromoSensitive", "HighSpender"]
let merged = oldTags.union(newTags)
let common = oldTags.intersection(newTags)
print("Merged: \(merged)")
print("Common: \(common)")
(3) Resultado: 20 minutos → 0,5 segundos
| Métrica | Laço com Array | Set/Dicionário |
|---|---|---|
| Desduplicação de 200 mil usuários | 20 minutos | 0,5 segundos |
| Linhas de código | 500+ | 30 |
| Uso de memória | 200 MB | 45 MB |
| Operação de união | Laço escrito à mão | .union() uma linha |
3. Sets
Um Set é uma coleção não ordenada de elementos únicos. Arrays se preocupam com "ordem e repetição"; Sets se preocupam com "unicidade e pertinência".
graph TB
A[Conjunto A] --- B["{1, 2, 3}"]
C[Conjunto B] --- D["{2, 3, 4}"]
E[União] --- F["{1, 2, 3, 4}"]
G[Interseção] --- H["{2, 3}"]
I[Diferença Simétrica] --- J["{1, 4}"]
K[Subtração] --- L["A - B = {1}"]
| Operação de Set | Método Swift | Resultado |
|---|---|---|
| União | union(_:) | Todos os elementos de ambos os conjuntos |
| Interseção | intersection(_:) | Elementos compartilhados por ambos os conjuntos |
| Diferença | subtracting(_:) | Elementos em A mas não em B |
| Diferença simétrica | symmetricDifference(_:) | Elementos em exatamente um conjunto |
| É subconjunto | isSubset(of:) | Todos os elementos de A estão em B |
| Contém | contains(_:) | Verificação de pertinência O(1) |
(1) Criando e Operações Básicas
var fruits: Set<String> = ["Apple", "Banana", "Orange"]
fruits.insert("Apple")
fruits.insert("Grape")
fruits.remove("Banana")
print(fruits.contains("Apple"))
print(fruits.count)
(2) Operações de Conjunto
let a: Set = [1, 2, 3, 4, 5]
let b: Set = [4, 5, 6, 7, 8]
print("Union: \(a.union(b).sorted())")
print("Intersection: \(a.intersection(b).sorted())")
print("A - B: \(a.subtracting(b).sorted())")
print("Symmetric Diff: \(a.symmetricDifference(b).sorted())")
▶ Exemplo: Sistema de Gerenciamento de Tags de Usuário
// ============================================
// Managing user tags with Sets
// ============================================
var userTags: Set<String> = ["VIP", "NewUser", "HighSpender"]
let campaignTags: Set = ["VIP", "PromoSensitive"]
let excludeTags: Set = ["Inactive", "Fraud"]
userTags.insert("iOSUser")
userTags.insert("VIP")
let targetUsers = campaignTags.subtracting(excludeTags)
print("Target tags: \(targetUsers)")
let vipHighSpender = userTags.intersection(["VIP", "HighSpender"])
print("VIP high spenders: \(vipHighSpender)")
let allActive = userTags.union(campaignTags).subtracting(excludeTags)
print("All active tags: \(allActive.sorted())")
Saída:
TEXT 📖 Somente leituraTarget tags: ["VIP", "PromoSensitive"] VIP high spenders: ["VIP", "HighSpender"] All active tags: ["HighSpender", "iOSUser", "NewUser", "PromoSensitive", "VIP"]
4. Dicionários
Um Dicionário é uma coleção não ordenada de pares chave-valor, cada chave mapeando unicamente para um valor. Ideal para cenários de "consulta por nome".
graph TB
A[Dicionário] --> B["Chave: Maçã -> Valor: 3"]
A --> C["Chave: Banana -> Valor: 5"]
A --> D["Chave: Laranja -> Valor: 2"]
B --> E[Consulta O(1) por chave]
| Operação | Sintaxe | Observações |
|---|---|---|
| Criar | TipoChave: TipoValor | Dicionário vazio |
| Literal | ["a": 1, "b": 2] | Com valores iniciais |
| Ler | dict["chave"] | Retorna Optional |
| Atribuir | dict["chave"] = valor | Adicionar ou atualizar |
| Excluir | dict["chave"] = nil | Remover o par chave-valor |
| Percorrer | for (chave, valor) in dict |
Iterar todos |
(1) Criando e Modificando
var scores: [String: Int] = [:]
var populations = [
"New York": 8_336_817,
"Los Angeles": 3_979_576,
"Chicago": 2_693_976
]
populations["Houston"] = 2_320_268
populations["New York"] = 8_400_000
populations["Chicago"] = nil
if let nyPopulation = populations["New York"] {
print("NY population: \(nyPopulation)")
}
(2) Percorrendo Dicionários
let capitals = [
"USA": "Washington DC",
"UK": "London",
"Japan": "Tokyo",
"France": "Paris"
]
for (country, city) in capitals {
print("\(country): \(city)")
}
print("Countries: \(capitals.keys.sorted())")
print("Capitals: \(capitals.values.sorted())")
### ▶ Exemplo: Gerenciamento de Estoque
```swift
// ============================================
// Managing inventory with Dictionary
// ============================================
var inventory: [String: Int] = [
"Laptop": 15,
"Mouse": 50,
"Keyboard": 30
]
inventory["Monitor"] = 10
inventory["Mouse"]! += 20
if let laptopStock = inventory["Laptop"], laptopStock > 0 {
inventory["Laptop"] = laptopStock - 1
print("Shipped 1 Laptop")
}
let product = "Tablet"
if let stock = inventory[product] {
print("\(product): \(stock) units")
} else {
print("\(product) not found in inventory")
}
print("\n=== Inventory Report ===")
for (product, quantity) in inventory {
let status = quantity < 20 ? "Low stock" : "In stock"
print("\(product): \(quantity) units -- \(status)")
}
Saída:
TEXT 📖 Somente leituraEnviado 1 Notebook Tablet não encontrado no estoque === Relatório de Estoque === Notebook: 14 unidades -- Estoque baixo Mouse: 70 unidades -- Em estoque Teclado: 30 unidades -- Em estoque Monitor: 10 unidades -- Estoque baixo
5. Protocolo Hashable e Coleções Aninhadas
As chaves de Dicionário e os elementos de Set devem estar em conformidade com o protocolo Hashable — o Swift usa valores hash para consulta rápida.
| Tipos Hashable Integrados | Quando a Conformidade Manual é Necessária |
|---|---|
| String, Int, Double, Bool | Structs/classes personalizadas |
| Array (com elementos hashable) | Enums com valores associados |
| Set, Dictionary (como valores) | Tipos contendo propriedades não hashable |
(1) Tipos Personalizados como Chaves
struct Product: Hashable {
let id: Int
let name: String
}
var cart: [Product: Int] = [:]
let laptop = Product(id: 1001, name: "Laptop")
cart[laptop] = 2
print("Cart items: \(cart.count)")
(2) Coleções Aninhadas: Dicionário de Sets
var cityTags: [String: Set<String>] = [
"Paris": ["Eiffel Tower", "Louvre"],
"Tokyo": ["Shibuya", "Sensoji"]
]
cityTags["Paris"]?.insert("Arc de Triomphe")
cityTags["London"] = ["Big Ben", "Tower Bridge"]
for (city, landmarks) in cityTags {
print("\(city): \(landmarks.sorted().joined(separator: ", "))")
}
▶ Exemplo: Estatísticas de Grupos de Usuários
// ============================================
// User group stats with Dictionary and Set
// ============================================
let userLanguages: [String: Set<String>] = [
"Alice": ["Swift", "Python", "JavaScript"],
"Bob": ["Python", "Java", "Go"],
"Charlie": ["Swift", "Kotlin", "JavaScript"],
"Diana": ["Java", "C#", "Python"]
]
let swiftUsers = userLanguages.filter { $0.value.contains("Swift") }
print("Swift developers: \(swiftUsers.count)")
let allLanguages = userLanguages.values.reduce([]) { $0.union($1) }
print("All languages: \(allLanguages.sorted())")
let fullStack = userLanguages.filter { $0.value.count >= 3 }
for (name, langs) in fullStack {
print("Full stack: \(name) -- \(langs.sorted().joined(separator: ", "))")
}
Saída:
TEXT 📖 Somente leituraSwift developers: 2 All languages: ["C#", "Go", "Java", "JavaScript", "Kotlin", "Python", "Swift"] Full stack: Alice -- JavaScript, Python, Swift Full stack: Charlie -- JavaScript, Kotlin, Swift Full stack: Diana -- C#, Java, Python
6. Exemplo Completo: Sistema de Marcação de Perfis de Usuário
// ============================================
// User profile tagging system
// Combining Set and Dictionary concepts
// ============================================
import Foundation
struct UserProfile: Hashable {
let id: Int
let name: String
var tags: Set<String>
}
var users: [Int: UserProfile] = [
1: UserProfile(id: 1, name: "Alice", tags: ["VIP", "HighSpender", "iOS"]),
2: UserProfile(id: 2, name: "Bob", tags: ["NewUser", "Android"]),
3: UserProfile(id: 3, name: "Charlie", tags: ["VIP", "Android", "HighSpender"]),
4: UserProfile(id: 4, name: "Diana", tags: ["iOS", "NewUser"])
]
let campaignTags: Set = ["VIP", "iOS"]
let excludeTags: Set = ["Fraud", "Inactive"]
var targetUserIds: Set<Int> = []
for (id, profile) in users {
let effectiveTags = profile.tags.subtracting(excludeTags)
if !effectiveTags.intersection(campaignTags).isEmpty {
targetUserIds.insert(id)
}
}
print("=== Campaign Target Users ===")
for id in targetUserIds.sorted() {
if let user = users[id] {
print("\(user.name) -- tags: \(user.tags.sorted().joined(separator: ", "))")
}
}
print("\n=== Adding Tag: BetaTester ===")
for id in users.keys {
users[id]?.tags.insert("BetaTester")
}
print("\n=== Tag Distribution ===")
var tagCounts: [String: Int] = [:]
for (_, profile) in users {
for tag in profile.tags {
tagCounts[tag, default: 0] += 1
}
}
for (tag, count) in tagCounts.sorted(by: { $0.value > $1.value }) {
print("\(tag): \(count) users")
}
Saída:
TEXT 📖 Somente leitura=== Usuários Alvo da Campanha === Alice -- tags: AltoGasto, iOS, VIP Charlie -- tags: AltoGasto, Android, VIP Diana -- tags: iOS, NovoUsuario === Adicionando Tag: TestadorBeta === === Distribuição de Tags === TestadorBeta: 4 usuários Android: 2 usuários AltoGasto: 2 usuários iOS: 2 usuários NovoUsuario: 2 usuários VIP: 2 usuários
❓ Perguntas Frequentes
P: Como escolher entre Set e Array? R: Use Set quando precisar de unicidade, consulta rápida (O(1)) e não se importar com a ordem. Use Array quando precisar de ordenação, permitir duplicatas e acesso baseado em índice. P: O que um Dicionário retorna quando uma chave não é encontrada? R: Retorna nil (Optional). Sempre trate as consultas ao dicionário com if-let ou o operador ??. P: O que é Hashable e por que as chaves de Set e Dicionário precisam dele? R: Hashable significa que o tipo pode ser hasheado. O Swift usa valores hash para localizar elementos rapidamente (complexidade O(1)). Int, String e outros tipos básicos estão em conformidade com Hashable por padrão. P: O que devo observar ao usar uma struct personalizada como chave de dicionário? R: Deve estar em conformidade com Hashable. Se todas as propriedades armazenadas forem Hashable, o Swift sintetiza automaticamente o método hash. Caso contrário, implemente hash(into:) e == manualmente. P: O que as propriedades keys e values do Dicionário retornam? R: Retornam tipos de coleção — keys é Dictionary.Keys (usável como um Set), values é Dictionary.Values (usável como um Array). Ambos podem ser percorridos diretamente.
📖 Resumo
- Set é uma coleção não ordenada de elementos únicos, ideal para desduplicação e verificações de pertinência
- Set suporta operações de união, interseção, subtração e diferença simétrica
- Dicionário é uma coleção não ordenada de pares chave-valor; a leitura retorna Optional
- As chaves de Dicionário e os elementos de Set devem estar em conformidade com Hashable
- Adicione entradas ao dicionário atribuindo um valor; remova definindo como nil
- Coleções aninhadas (Dicionário de Sets) permitem modelagem de dados complexa
📝 Exercícios
- Iniciante: Crie um Set com seus 5 livros favoritos. Verifique se "Programação Swift" está nele, adicione 2 novos livros e imprima o conjunto final.
- Intermediário: Implemente um dicionário simples Inglês-Português usando Dicionário. Adicione 5 palavras com traduções, implemente uma função de consulta (entrada em inglês, retorno em português) e trate os casos em que uma palavra não é encontrada.
- Desafio: Analise um conjunto de dados: ["Alice": ["Swift", "Python"], "Bob": ["Java", "Swift"], "Charlie": ["Python", "Go"], "Diana": ["Swift", "Go"]]. Encontre usuários que conhecem Swift mas não Go, usuários que conhecem Python ou Java, o conjunto de todas as linguagens (desduplicado) e a contagem de usuários por linguagem.