Set — это неупорядоченная коллекция уникальных элементов одного типа.
// Сравним, уже известный нам Массив и Сет:
let fruits: [String] = ["🍎", "🍌", "🍎"] // ["🍎", "🍌", "🍎"] (дубликат есть)
let fruitsSet: Set<String> = ["🍎", "🍌", "🍎"] // ["🍎", "🍌"] (дубликат удален)
Прежде всего польза Set в том, что мы можем ответить на вопрос – “Хранится ли у нас такой элемент ?” – мгновенно.
Дело в том: чтобы проверить есть ли элемент в массиве, нам необходимо пройтись по каждому из элементу прежде, чем мы найдём нужный. И, скажем, если мы ищем букву “Я”, то нам придётся пройтись по всему алфавиту, прежде, чем мы убедимся, что такой буквы нет – это долго.
А представьте, что у нас массив из миллиона элементов, а нужный нам в самом конце ? – ужас.
В Set, благодаря тому, что он может хранить только Hashable, уникальные элементы – мы можем проверить наличие элемента мгновенно. (О Hashable мы поговорим позже)
| Тема | Описание |
|---|---|
| 🎯 Основы | Что такое Set и чем он отличается от массива |
| 🤔 Применение | Когда удобно использовать Set вместо других коллекций |
| 🏗️ Создание | Как объявлять и инициализировать Set |
| ➕ Добавление | Как добавлять элементы в Set |
| 🗑️ Удаление | Как удалять элементы из Set |
| 🔍 Проверка | Как быстро проверить наличие элемента |
| 🔄 Перебор | Как проходить по элементам в Set |
| 🧩 Сложные типы | Как хранить в Set структуры и классы |
| 🧮 Операции | Математика множеств: пересечения, объединения |
Представьте, что у вас есть ящик с инструментами 🧰:
Ящик содержит: [🔧, 🔨, 🪛, 🔩]
Главные правила такого ящика:
| Характеристика | Массив (Array) |
Множество (Set) |
|---|---|---|
| Порядок | ✅ Есть (Индексы) | ❌ Случайный |
| Индексы | ✅ Есть (array[2]) |
❌ Нет |
| Дубликаты | ✅ Есть | ❌ Нет |
| Скорость поиска | 🐢 Медленная (проверяет все) | ⚡ Мгновенная |
| Аналогия | Полка с книгами 📚 | Ящик с инструментами 🧰 |
⚠️ Запомните: Пожалуйста эти ключевые понятия выше, они важны
Сравнение:
// Массив — порядок важен
let playlist = ["Песня 1", "Песня 2", "Песня 3"]
print(playlist[1]) // Всегда "Песня 2"
// Множество — порядок не важен, дубликатов нет
let uniqueTracks: Set = ["Песня 1", "Песня 2", "Песня 1"]
print(uniqueTracks) // ["Песня 2", "Песня 1"] (порядок случаен, дубликат удалён)
| Ситуация | Пример | Почему Set? |
|---|---|---|
| Уникальные ID | ID пользователей, заказов | Автоматически удаляет дубликаты |
| Проверка наличия | Есть ли товар на складе? | Мгновенный поиск (contains) |
| Теги / Категории | Хэштеги, Категории товаров | Нет повторов, быстрый поиск |
| Математические операции | Общие друзья, пересечение аудиторий | Встроенные методы (intersection, union) |
⚠️ Запомните: Преимущество Сета – моментальная проверка наличия элемента, хранит только уникальные элементы
// ❌ Плохо: используем массив для уникальных данных
// Представьте, что у нас есть группа в ТГ, очевидно, что один участник не может повторяться
// Мы можем в качестве хранилища выбрать Set – в таком случае защитимся наверняка от дубликатов
var userIdsArray: [Int] = [101, 102, 103, 101, 104, 102]
// ✅ Оптимально: используем Set
// Мы можем не писать доп. логики для проверки на дубликаты, а просто использовать Set
var userIdsSet: Set<Int> = [101, 102, 103, 101, 104, 102]
// Уже [101, 102, 103, 104] — дубликаты удалены автоматически, а доступ к ним моментален
⚠️ Запомните: ещё раз Преимущество Set – моментальная проверка наличия элемента, только уникальные элементы
// Способ 1: с указанием типа
var emptyStrings: Set<String> = [] // Пустое множество строк
var emptyNumbers: Set<Int> = [] // Пустое множество чисел
// Swift сам понимает тип
var fruits: Set = ["Яблоко", "Мандарин", "Манго"] // Set<String>
var scores: Set = [5, 4, 5, 3, 5] // Set<Int> → [3, 4, 5]
// С явным указанием типа (если не указать явно – Swift подумает, что это Массив, вот он тупой)
var explicitSet: Set<Int> = [1, 2, 3, 3, 3] // [1, 2, 3]
🧠 Важно! Даже если вы укажете дубликаты при создании,
Setавтоматически их удалит:let setWithDuplicates = [1, 1, 2, 2, 3, 3] // [2, 3, 1] (порядок случаен, дубликатов нет (ещё раз повторим))
let vs var// var — можно изменять
var mutableSet: Set = [1, 2, 3]
mutableSet.insert(4) // ✅ Работает!
// let — нельзя изменять
let immutableSet: Set = [1, 2, 3]
immutableSet.insert(4) // ❌ Ошибка!
var fruits: Set = ["🍎 Яблоко", "🍌 Бананчек"]
// Добавляем новый элемент (через insert, append есть только у Массива)
fruits.insert("🍊 Мандарин")
print("Стало: \(fruits)") // ["🍎 Яблоко", "🍌 Бананчек", "🍊 Мандарин"] (порядок случаен)
var shopping: Set = ["🥛 Молоко", "🍞 Хлеб", "🧀 Сыр", "🍎 Яблоко"]
if let removed = shopping.remove("🍞 Хлеб") {
print("Удалили: \(removed)") // Удалили: 🍞 Хлеб
print("Осталось: \(shopping)") // Осталось: ["🥛 Молоко", "🧀 Сыр", "🍎 Яблоко"]
}
Обратите внимание – чтобы удалить элемент из массива, нам нужно знать его индекс, но в Set нет индекса – мы моем сразу удалить элемент.
shopping.removeAll()
print(shopping) // []
В массиве останутся, только те элементы, которые прошли проверку в .filter...
let numbers: Set = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
let evenNumbersArray = numbers.filter { number in
number % 2 == 0 // Остаток от деления, нужны только чётные значения (4 % 2 = 0 / 5 % 2 = 1)
}
// evenNumbersArray — это [Int], а не Set<Int> – Обратите внимание
// Превращаем массив обратно в Set
let evenNumbersSet = Set(evenNumbersArray)
⚠️ Запомните: Превратив массив в сет, вы моментально избавитесь от дубликатов
let availableColors: Set = ["🔴 Красный", "🟢 Зеленый", "🔵 Синий", "🟡 Желтый"]
// contains() — проверяет наличие
let hasRed = availableColors.contains("🔴 Красный") // true
let hasBlack = availableColors.contains("⚫ Черный") // false
print("Красный есть? \(hasRed)") // Красный есть? true
print("Чёрный есть? \(hasBlack)") // Чёрный есть? false
⚡ Важно! contains в Set работает мгновенно, а в Array — медленно:
- Array: проверяет каждый элемент по очереди O(n)
- Set: моментальный ответ O(1)
let colors: Set = ["🔴", "🟢", "🔵", "🟡"]
// Простой перебор
for color in colors {
print("Цвет: \(color)")
}
// Цвет: 🔵
// Цвет: 🟢
// Цвет: 🔴
// Цвет: 🟡
⚠️ Запомните: Порядок может быть другим при каждом запуске (Элементы в Сете хранятся в случайно порядке)
HashableЧтобы Set мог проверять уникальность, тип должен поддерживать протокол Hashable.
Примитивные типы данных автоматически поддерживают Hashable:
Int, String, Double, Bool)Hashableenum)Как реализовать протокол Hashable самостоятельно, мы поговорим позже
struct Car: Hashable {
let brand: String
let model: String
let year: Int
}
var cars: Set<Car> = []
let car1 = Car(brand: "BMW", model: "X5", year: 2020)
let car2 = Car(brand: "Audi", model: "Q7", year: 2021)
let car3 = Car(brand: "BMW", model: "X5", year: 2020) // Полная копия car1
cars.insert(car1)
cars.insert(car2)
cars.insert(car3) // Не добавился (уже есть car1, у которого все поля совпадают)
print("Уникальных машин: \(cars.count)") // 2
Обратите внимание – подписав нашу Car под Hashable, мы можем хранить Авто в сете, иначе не получится. С примитивными типами данных (String, Int, Bool…) всё проще – они уже под Hashable, чтобы хранить их в Set ничего дополнительно делать не нужно
Это главная суперсила Set!
Используется не часто, хорошо просто знать, что так можно.
let swiftDevelopers: Set = ["Анна", "Иван", "Ольга", "Петр"]
let kotlinDevelopers: Set = ["Иван", "Петр", "Елена", "Дмитрий"]
intersection)Элементы, которые есть и там, и там
let bothLanguage = swiftDevelopers.intersection(kotlinDevelopers)
print("Знают оба языка: \(bothLanguage)") // ["Иван", "Петр"]
union)Все элементы из обоих множеств, без повторов
let allDevelopers = swiftDevelopers.union(kotlinDevelopers)
print("Все разработчики: \(allDevelopers)")
// ["Анна", "Иван", "Ольга", "Петр", "Елена", "Дмитрий"]
subtracting)Элементы из первого множества, которых нет во втором
let onlySwift = swiftDevelopers.subtracting(kotlinDevelopers)
print("Знают только Swift: \(onlySwift)") // ["Анна", "Ольга"]
symmetricDifference)Элементы, которые есть только в одном из множеств
let exclusive = swiftDevelopers.symmetricDifference(kotlinDevelopers)
print("Знают только один язык: \(exclusive)")
// ["Анна", "Ольга", "Елена", "Дмитрий"]
let groupA: Set = [1, 2, 3]
let groupB: Set = [1, 2, 3, 4, 5]
let groupC: Set = [6, 7, 8]
// Подмножество (все элементы A есть в B?)
print(groupA.isSubset(of: groupB)) // true
// Надмножество (B содержит все элементы A?)
print(groupB.isSuperset(of: groupA)) // true
// Не пересекаются (нет общих элементов?)
print(groupA.isDisjoint(with: groupC)) // true
| Действие | Код | Пояснение |
|---|---|---|
| Создать пустой сет | var emptySet: Set<Int> = [] |
Пустое множество чисел |
| С данными | var dataSet: Set = [1, 2, 3] |
Из трех чисел |
| Добавить | dataSet.insert(4) |
Добавляет 4 |
| Удалить | dataSet.remove(2) |
Удаляет 2 |
| Удалить все | dataSet.removeAll() |
Очищает |
| Проверить наличие | dataSet.contains(3) |
true, если есть 3 |
| Количество | dataSet.count |
Сколько элементов |
| Проверить пустоту | dataSet.isEmpty |
true, если пусто |
// ❌ Это массив, а не множество
let wrong = [1, 2, 3, 3, 3] // [Int] — массив с дубликатами
// ✅ Это множество
let correct: Set<Int> = [1, 2, 3, 3, 3] // Set<Int> — без дубликатов
let thisIsSet: Set<String> = ["a", "b", "c"]
let element = thisIsSet[1] // ❌ Ошибка! У Set нет индексов
// ✅ Надо перебирать или использовать .contains
if thisIsSet.contains("b") {
print("b есть в множестве")
}
let thisIsSet: Set = [1, 2, 3]
thisIsSet(4) // ❌ Ошибка! let = неизменяемое
// ✅ Надо использовать var
var mutableSet: Set = [1, 2, 3]
mutableSet.insert(4) // ✅ Работает!
let set: Set = [1, 2, 3, 4, 5]
// Не надейтесь, что элементы будут в порядке 1,2,3,4,5!
for item in set {
print(item) // Может быть: 3, 5, 1, 4, 2
}
| Нужно | Выбор | Почему |
|---|---|---|
| Сохранить порядок | Array |
У массива есть индексы |
| Быстрый поиск | Set |
contains работает мгновенно |
| Уникальные значения | Set |
Автоматически удаляет дубликаты |
| Дубликаты разрешены | Array |
Массив разрешает дубликаты |
| Математические операции | Set |
intersection, union и т.д. |
| Доступ по индексу | Array |
array[2] — легко! |
Что будет в множестве после выполнения кода?
let thisIsSet: Set = [1, 2, 2, 3, 3, 3]
print(thisIsSet)
Можно ли получить элемент по индексу из Set?
Что вернет set.contains() если элемента нет?
Какая операция найдет общие элементы двух множеств?
TGUserтелефонном номере, никнейме и роли пользователя (admin, user, guest)enum.
Подумай – почему для роли удобно выбрать enum, а не просто StringКогда пользователь хочет вступить в ту или иную группу в ТГ – перед ним есть кнопка Вступить / Выйти
Необходимо написать функцию, которая будет проверять есть ли данный пользователь в списке участников групп.
И если он есть – функция должна возвращать текст Выйти, если пользователя нет в списке – Вступить
Решение можете присылать сюда