Array) в Swift 📚Массив — это упорядоченная коллекция элементов одного типа.
Проще говоря, это пронумерованный список, где у каждого элемента есть свой порядковый номер, который называется индексом.
// Индексы: 0 1 2 3
let shoppingList = ["🥛 Молоко", "🍞 Хлеб", "🧀 Сыр", "🍎 Яблоки"]
| Тема | Описание |
|---|---|
| 📦 Основы | Что такое массив и зачем он нужен |
| 🏗️ Создание | Как объявлять и инициализировать массивы |
| ➕ Добавление | Как вставлять элементы в массив |
| 🔍 Поиск | Как находить элементы и их индексы |
| 🗑️ Удаление | Как удалять и фильровать элементы |
| ✏️ Изменение | Как менять существующие элементы |
| 🎭 Разные типы | Как хранить разные типы в одном массиве |
Представьте, что вы ведёте список покупок в заметках:
📝 Список покупок:
─────────────────
0. 🥛 Безлактозное молоко
1. 🍞 Хлеб
2. 🧀 Сыр
3. 🍎 Яблоки
В Swift такой список называется массивом (Array)
Это пронумерованный список, где у каждого элемента есть свой порядковый номер (индекс).
🔢 Важно: Отсчёт всегда начинается с нуля, а не с единицы!
Массив может хранить элементы только ОДНОГО ТИПА. Нельзя смешивать строки и числа.
✅ Правильно:
let shoppingList = ["Молоко", "Хлеб", "Сыр"] // 👌 Только строки
let prices = [143, 11, 27] // 👌 Только числа
❌ Неправильно:
let mixed = ["Молоко", 100, "Хлеб"] // ❌ Ошибка! Разные типы
Элементы хранятся в том порядке, в котором вы их добавили.
let fruits = ["Яблоко", "Банан", "Апельсин"]
print(fruits[0]) // "Яблоко" всегда первое
print(fruits[1]) // "Банан" всегда второй
print(fruits[2]) // "Апельсин" всегда третий
print(fruits[3]) // "Краш приложения" всегда
В отличие от множества (Set), массивы разрешают дубликаты. (О Set мы поговорим позднее)
let votes = [5, 5, 3, 5, 4, 5] // Число 5 встречается 4 раза
print("Всего оценок: \(votes.count)") // 6
Это самая частая ошибка у новичков!
// Индексы: 0 1 2 3
let letters = ["A", "B", "C", "D"]
letters[0] // "A" (первый элемент) ✅
letters[1] // "B" (второй элемент) ✅
letters[4] // 💥 ОШИБКА! Индекс за пределами массива!
⚠️ Запомните: Последний элемент всегда имеет индекс
array.count - 1
Всегда проверяйте, что индекс существует!
let colors = ["белый", "синий", "красный"]
let index = 5 // Хотим получить элемент с индексом 5
// ✅ Безопасная проверка:
if index < colors.count { // Убедимся, что массив боьше, чем запрашиваемый нами индекс
print(colors[index]) // Этот код выполнится, только если индекс существует
} else {
print("❌ Индекс \(index) вне диапазона. Допустимые индексы: 0...\(colors.count - 1)")
}
// Выведет: ❌ Индекс 5 вне диапазона. Допустимые индексы: 0...2
💀 Запомните: Если обратиться по несуществующему индексу — приложение упадёт!
// Способ 1: с указанием типа
var emptyStrings: [String] = [] // Пустой массив строк
var emptyNumbers: [Int] = [] // Пустой массив чисел
// Swift сам понимает тип (type inference) исходя из данных
let numbers = [1, 2, 3, 4, 5] // Swift понял: это [Int]
let names = ["Виктория", "Сергей"] // Swift понял: это [String]
// Можно указать тип явно (но это моветон, лучше не указывать)
let explicitNumbers: [Int] = [1, 2, 3]
// Массив с повторяющимися значениями
let zeros = Array(repeating: 0, count: 5) // [0, 0, 0, 0, 0]
let stars = Array(repeating: "⭐", count: 3) // ["⭐", "⭐", "⭐"]
let и var// 🟢 var — массив можно изменять
var mutableArray = [1, 2, 3]
mutableArray.append(4) // ✅ Работает! [1, 2, 3, 4]
mutableArray[0] = 10 // ✅ Работает! [10, 2, 3, 4]
// 🔴 let — массив НЕЛЬЗЯ менять
let immutableArray = [1, 2, 3]
immutableArray.append(4) // ❌ Ошибка компиляции!
immutableArray[0] = 10 // ❌ Ошибка компиляции!
🔑 Запомните:
let= константа (только чтение) 📖var= переменная (можно изменять) ✏️
Рассмотрим пример:
var students = ["Павел", "Еблантий"]
// 📌 append() — добавляет элемент в КОНЕЦ массива
students.append("Мария") // ["Павел", "Иблантий", "Мария"]
// 📌 insert(at:) — вставляет элемент на конкретную позицию
students.insert("Денис", at: 1) // ["Павел", "Денис", "Иблантий", "Мария"]
// 📌 append(contentsOf:) — добавляет несколько элементов в конец массива
let newStudents = ["Галина", "Фёдор"]
students.append(contentsOf: newStudents) // ["Павел", "Денис", "Иблантий", "Мария", "Галина", "Фёдор"]
📝 Шпаргалка по добавлению:
append()→ в конец (один элемент)append(contentsOf:)→ в конец (несколько)insert(at:)→ на конкретную позицию
let colors = ["🔴 Красный", "🟢 Зеленый", "🔵 Синий"]
// ✅ Существующие индексы
let firstColor = colors[0] // "🔴 Красный"
let secondColor = colors[1] // "🟢 Зеленый"
let thirdColor = colors[2] // "🔵 Синий"
// 💥 НЕСУЩЕСТВУЮЩИЙ ИНДЕКС — КРАШ!
let fourthColor = colors[3] // Fatal error: Index out of range
// ✅ Способ 1: проверка границ
let index = 2
if index < colors.count {
print(colors[index]) // "🔵 Синий"
} else {
print("Индекс вне диапазона")
}
// ✅ Способ 2: first и last (возвращают Optional)
if let first = colors.first {
print("Первый цвет: \(first)") // "🔴 Красный"
}
if let last = colors.last {
print("Последний цвет: \(last)") // "🔵 Синий"
}
// ✅ Способ 3: Модный – indices, безопасный диапазон
// indices это все доступные индексы в данном массиве
if colors.indices.contains(2) {
print(colors[2]) // "🔵 Синий"
}
🛡️ Правило безопасности: Всегда проверяйте существование индекса перед обращением!
var grades = [4, 5, 3, 5]
if grades.indices.contains(2) { // Безопасность, превыше всего
grades[2] = 4 // Заменяем тройку на четверку – [4, 5, 4, 5]
}
⚠️ Важно: При изменении всегда убеждайтесь, что индекс существует!
var playlist = ["Песня 1", "Песня 2", "Песня 3", "Песня 4"]
// 📌 remove(at:) — удаляет по индексу (возвращает удаленный элемент)
let removedSong = playlist.remove(at: 1)
print("🗑️ Удалили: \(removedSong)") // "Песня 2"
print("🎵 Осталось: \(playlist)") // ["Песня 1", "Песня 3", "Песня 4"]
// 📌 removeFirst() — удаляет первый элемент
let firstRemoved = playlist.removeFirst()
print("🗑️ Удалили первый: \(firstRemoved)"). // "Песня 1"
print("🎵 Осталось: \(playlist)") // ["Песня 3", "Песня 4"]
// 📌 removeLast() — удаляет последний элемент
let lastRemoved = playlist.removeLast()
print("🗑️ Удалили последний: \(lastRemoved)") // "Песня 4"
print("🎵 Осталось: \(playlist)") // ["Песня 3"]
// 📌 removeAll() — удаляет ВСЁ!
playlist.removeAll()
print("🎵 После removeAll: \(playlist)") // []
let fruits = ["🍎 яблоко", "🍌 банан", "🍊 апельсин", "🍎 яблоко"]
let numbers = [10, 23, 45, 67, 89, 12, 34, 56]
// contains() — содержится ли элемент в массиве ?
let hasApple = fruits.contains("🍎 яблоко") // true
let hasGrape = fruits.contains("🍇 виноград") // false
// firstIndex(of:) — ищем индекс конкретного элемента (Первый, который нам встетится в массиве)
if let index = fruits.firstIndex(of: "🍌 банан") {
print("🍌 банан найден на позиции \(index)") // 1
}
// first(where:) — первый элемент, удовлетворяющий условию
if let firstEven = numbers.first(where: { number in
number % 2 == 0
}) {
print("Первое четное число: \(firstEven)") // 10
}
// firstIndex(where:) — индекс первого элемента по условию
if let index = numbers.firstIndex(where: { number in
number > 50
}) {
print("Первое число >50 имеет индекс \(index)") // 3 (число 67)
}
// filter — останутся только те элементы, которые удовлетворяют условию
let evenNumbers = numbers.filter { number in
number % 2 == 0 // % – Операция "Остаток от деления" (4 % 2 = 0 | 5 % 2 = 1 ... )
}
print("Все чётные числа: \(evenNumbers)") // [10, 12, 34, 56]
В массиве evenNumbers остались только чётные элементы
// allSatisfy — все ли элементы удовлетворяют условию?
let allPositive = [1, 2, 3, 4, 5].allSatisfy { $0 > 0 } // true
let allEven = [2, 4, 6, 7, 8].allSatisfy { $0 % 2 == 0 } // false
print("Все числа положительные? \(allPositive)")
print("Все числа четные? \(allEven)")
$0 это более короткая запись ` number in`
Как вы заметили ` number in мы вовсе опустили и оставили только $0`
Давай рассмотрим следущий пример:
var unsorted = [3, 1, 4, 2, 5]
let unsortedCopy = unsorted
// sort() — сортирует массив на месте (меняет оригинал)
unsorted.sort() // [1, 2, 3, 4, 5]
// Сортировка по убыванию
unsorted.sort(by: >) // [5, 4, 3, 2, 1]
// sorted() — возвращает отсортированную копию
let sorted = unsortedCopy.sorted()
print("📊 Оригинал: \(unsortedCopy)") // [3, 1, 4, 2, 5] (не изменился)
print("📊 Копия: \(sorted)") // [1, 2, 3, 4, 5]
// Сортировка строк
let names = ["Иван", "Анна", "Борис", "Дмитрий"]
let sortedNames = names.sorted()
print("📊 Имена по алфавиту: \(sortedNames)") // ["Анна", "Борис", "Дмитрий", "Иван"]
🔑 Запомните:
sort()– меняет оригиналsorted()– создаёт копию
Массив для экспериментов:
let array = [1, 2, 3, 4, 5, 3, 2, 1]
array.isEmpty // false (пустой ли массив)
array.count // 8 (количество элементов)
array.first // Optional(1) (первый элемент)
array.last // Optional(1) (последний элемент)
// reversed() — разворачивает массив (В обратном порядке, наизнанку)
let array = [1, 2, 3, 4, 5]
let reversed = array.reversed() // [5, 4, 3, 2, 1]
// shuffled() — перемешивает элементы
let shuffled = array.shuffled() // [3, 4, 5, 1, 2]
Представьте, что мы пишем приложение для школы. У нас есть разные типы людей:
struct Student {
let name: String
let scores: [Int] // Оценки ученика
}
struct Teacher {
let name: String
let subject: String // Предмет
let salary: Int // Зарплата
}
struct Director {
let name: String
let experience: Int // Опыт работы
}
Как хранить их ВСЕХ в одном списке? 🤔
protocol)Протокол — это как контракт.
Если структура подписывает протокол, она ОБЯЗАНА выполнить его требования.
/// 📋 Протокол, объединяющий всех людей в школе
protocol Person {
var name: String { get } // Можно только читать
var surname: String { get set } // Можно читать и изменять
}
📖 Расшифровка:
{ get }— свойство можно только читать (поле должно бытьlet){ get set }— свойство можно и читать, и изменять (поле должно бытьvar)
struct Student: Person {
let name: String
var surname: String
let scores: [Int]
}
struct Teacher: Person {
let name: String
var surname: String
let subject: String
let salary: Int
}
struct Director: Person {
let name: String
var surname: String
let experience: Int
}
Важно понимать, что теперь это не массив конкретных Student, Teacher и Director это всё лишь наш протокол Person, т.е. все элементы массива это прежде всего Person
// Тип массива — [Person], но внутри могут быть любые типы, подписанные под Person
let schoolMembers: [Person] = [
Student(name: "Павел", surname: "Петров", scores: [5, 4, 5]),
Teacher(name: "Василиса", surname: "Иванова", subject: "Математика", salary: 50_000),
Student(name: "Милена", surname: "Сидорова", scores: [4, 4, 5]),
Director(name: "Ангелина", surname: "Михайловна", experience: 15)
]
// Проходим по всем членам школы
for person in schoolMembers {
// У всех есть имя и фамилия (из протокола)
print("👤 \(person.name) \(person.surname)")
// Чтобы получить доступ к УНИКАЛЬНЫМ свойствам,
// нужно "привести" (скастить) к конкретному типу
if let student = person as? Student {
print("📚 Ученик, оценки: \(student.scores)")
} else if let teacher = person as? Teacher {
print("🍎 Учитель, предмет: \(teacher.subject)")
} else if let director = person as? Director {
print("👔 Директор, опыт: \(director.experience) лет")
}
}
⚠️ Важно: Постарайтесь осмыслить – данный код выполнится 4ре раза! и в каждом из прогонов будет разный
personВначале это
Student (Павел), затемTeacher (Василиса), вновьStuden (Милена)и на последокDirector (Ангелина)В итоге мы попадём во все
if letНО НЕ СРАЗУ, а по одному разу за каждый прогон и только в конкретныйif let, в зависимости от текущего типа данныхperson
// Найдем директора с опытом 15 лет
if let directorIndex = schoolMembers.firstIndex(where: { person in
// Проверяем, является ли человек директором И имеет ли опыт 15 лет
if let director = person as? Director, director.experience == 15 {
return true
}
return false
}) {
print("✅ Нашли директора по индексу \(directorIndex)")
// Меняем данные (нужен var, т.к. собираемся изменять)
if var director = schoolMembers[directorIndex] as? Director {
director.experience = 16
// Важно: director — это КОПИЯ! Нужно обновить в массиве оригинал
schoolMembers[directorIndex] = director
print("📈 Опыт увеличен до \(director.experience)")
}
} else {
print("❌ Директор с опытом 15 лет не найден")
}
⚠️ Важно: Внутри
firstIndexмы обязательно должны вернуть либоtrueлибоfalse
return trueзначит, что это именно тот элемент, который нам и нужен
Найденный нами индекс directorIndex может и не существовать (Представьте, что директора с опытом работы 15 лет мы не нашли) в таком случае в массиве нет нужного элемента, а соответственно и индекса, по которому он мог бы находиться.
В таком случае directorIndex будет равен nil
Поле directorIndex: Int? опционально (либо Int, либо nil) и нам необходимо убедиться, что оно всё же существует (Int) и в этом нам помогает запись if let directorIndex = schoolMembers...
Более подробно про опционалы мы уже говорили здесь
Какой индекс у первого элемента массива?
let array = [10, 20, 30, 40, 50]
Что произойдет при выполнении кода?
let numbers = [1, 2, 3, 4, 5]
print(numbers[5])
Что выведет этот код?
var fruits = ["🍎", "🍌", "🍊"]
fruits.append("🍇")
fruits.insert("🍓", at: 1)
print(fruits.count)
Какая разница между sort() и sorted()?
sort() меняет массив, sorted() создает новыйsort() для чисел, sorted() для строкЧто вернет [1, 2, 3, 2, 1].firstIndex(of: 2)?
Как безопасно получить первый элемент массива?
array[0]array.firstarray[1]Что выведет этот код?
var items = [1, 2, 3, 4, 5]
items.remove(at: 2)
items.removeFirst()
print(items)
Можно ли хранить в одном массиве Int и String?
На самом деле мы можем хранить разные типы, но Swift – строготипизированный язык, поэтому стоит избегать подобных практик
Что выведет этот код?
let numbers = [1, 2, 3, 4, 5, 6]
let result = numbers.filter { number in number % 3 == 0 }
print(result)
Как добавить элемент в начало массива?
array.append(10, at: 0)array.insert(10, at: 0)array.prepend(10)| Действие | Код | Результат |
|---|---|---|
| Пустой массив | var array: [Int] = [] |
[] |
| С данными | let array = [1, 2, 3] |
[1, 2, 3] |
| С повторением | Array(repeating: 0, count: 3) |
[0, 0, 0] |
| Действие | Код | Пояснение |
|---|---|---|
| В конец (1 элемент) | array.append(4) |
[1, 2, 3, 4] |
| В конец (несколько) | array += [5, 6] |
[1, 2, 3, 4, 5, 6] |
| В начало | array.insert(0, at: 0) |
[0, 1, 2, 3, 4, 5, 6] |
| Действие | Код | Пояснение |
|---|---|---|
| По индексу | array.remove(at: 2) |
Удаляет элемент с индексом 2 |
| Первый | array.removeFirst() |
Удаляет первый элемент |
| Последний | array.removeLast() |
Удаляет последний |
| Все | array.removeAll() |
Очищает массив |
| Действие | Код | Возвращает |
|---|---|---|
| Проверить наличие | array.contains(5) |
true/false |
| Найти индекс | array.firstIndex(of: 3) |
Optional(Int) |
| Найти по условию | array.first(where: { $0 > 5 }) |
Optional(Int) |
| Свойство | Код | Значение |
|---|---|---|
| Количество | array.count |
Int |
| Пустой? | array.isEmpty |
true/false |
| Первый | array.first |
Optional |
| Последний | array.last |
Optional |
let array = [10, 20, 30]
// ❌ Неправильно
let first = array[1] // Думают, что это первый, но это ВТОРОЙ!
// ✅ Правильно
let correct = array[0] // Первый элемент
let array = [1, 2, 3]
// ❌ Опасно!
let element = array[5] // 💥 Crash!
// ✅ Безопасно
if array.count > 5 {
let element = array[5]
} else {
print("Индекс вне диапазона")
}
// ❌ Ошибка!
let array = [1, 2, 3]
array.append(4) // Компилятор не пропустит
// ✅ Правильно
var mutableArray = [1, 2, 3]
mutableArray.append(4) // Работает!
var array = [1, 2, 3]
// ❌ Непонятно, что хотели
array.append(0) // Добавит в КОНЕЦ: [1, 2, 3, 0]
// ✅ Если хотели в начало:
array.insert(0, at: 0) // [0, 1, 2, 3]
let emptyArray: [Int] = []
// ❌ Опасно! first может быть nil
let first = emptyArray.first! // 💥 Crash на пустом массиве!
// ✅ Безопасно
if let first = emptyArray.first {
print(first)
} else {
print("Массив пуст")
}
PhonePhoneNotebookNotebookPhone и NotebookNotebookPhone, который тебе нужен (например модель iPhone 13)Notebook (Которое есть в протоколе)Phone поле (Которого нет в протоколе)Решение можете присылать сюда
Поздравляю! Вы освоили массивы — фундаментальную структуру данных. Теперь вы готовы к изучению:
| Тема | Описание |
|---|---|
| Множества | Как массивы, но без порядка и без повторов |
| Словари | Хранят пары “ключ-значение” |