JavaRush /Курси /Swift SELF /Створення масивів: Array<Element>

Створення масивів: Array<Element>

Swift SELF
Рівень 11 , Лекція 1
Відкрита

1. Що таке Array і які в нього правила

Якщо ви тільки починаєте, здається, що змінних достатньо: ну, зроблю book1, book2, book3 — і готовий «список книг». Але це швидко перетворюється на головний біль: як додати четверту книгу, як перебрати всі, як знайти потрібний елемент? Масив розв’язує цю проблему: він зберігає багато значень одного типу, у порядку додавання, і дає змогу писати універсальний код.

Уявіть, що ви ведете невеликий консольний застосунок «Список читання». Учора ви могли зберігати одну книгу в String. Сьогодні ви хочете зберігати список назв книг. Ось тут Array і виходить на сцену — як полиця, на яку можна поставити не одну, а багато книг.

Коли ви чуєте слово «масив», не плутайтеся в термінах. Досить запам’ятати дві прості речі.

  • Перша: масив зберігає елементи в певному порядку.
  • Друга: усі елементи масиву мають один і той самий тип. Ніяких «тут Int, тут String, а тут ще котик» — котик зʼявиться пізніше, коли ви опануєте інші інструменти (і, сподіваюся, не в бойовому середовищі).

Отже, масив можна описати так: «упорядкована колекція значень одного типу». Порядок важливий, бо «перший», «другий», «третій» — це реальна характеристика масиву. Згодом ви зможете отримувати елементи за позицією, тобто за індексом, перебирати їх у циклі та змінювати.

Індекс у масиві

Мало знати, що у вас є масив, — треба ще вміти працювати з його елементами. Щоб отримати елемент із масиву, потрібно знати його позицію. Ця позиція називається індексом.

Індекси у Swift (як і в багатьох мовах програмування) починаються з нуля, а не з одиниці. Спочатку це може виглядати незвично, але швидко стає природним.

Уявіть масив книг:

let books = ["Dune", "Foundation", "The Hobbit"] // три рядки, упаковані у вигляді масиву

Якщо пронумерувати елементи, вийде так:

Індекс Елемент
0
"Dune"
1
"Foundation"
2
"The Hobbit"

Тобто:

  • перший елемент має індекс 0
  • другий — 1
  • третій — 2

Кількість елементів можна дізнатися через count. А останній індекс завжди дорівнює count - 1.

print(books.count) // 3

Схема індексів

flowchart LR
    A["0 — Dune"] --> B["1 — Foundation"]
    B --> C["2 — The Hobbit"]

Головне правило: індекс завжди має бути в діапазоні від 0 до count - 1. Якщо спробувати звернутися до неіснуючого індексу, програма завершиться з помилкою.

Наприклад, у масиву з 3 елементів індекс 3 вже не існує.

var books = ["Dune", "Foundation", "The Hobbit"]
books[1] = "The Hitchhiker's Guide to the Galaxy"

print(books[0]) // Dune
print(books[1]) // The Hitchhiker's Guide to the Galaxy
print(books[2]) // The Hobbit

Саме тому порядок у масиві такий важливий: кожен елемент має свою позицію, і за цією позицією його можна отримати або змінити.

2. Створюємо масиви: основні способи

Літерал масиву

Найзручніший спосіб створити масив — написати його прямо в коді в квадратних дужках. Це називається літерал масиву.

let scores = [10, 20, 30]
print(scores) // [10, 20, 30]

Тут scores — масив цілих чисел. Зверніть увагу: ми не писали тип явно. Swift побачив елементи 10, 20, 30 і дійшов висновку: «ага, це Int, отже масив [Int]».

Приклад із рядками:

let authors = ["Le Guin", "Tolkien", "Asimov"]
print(authors) // ["Le Guin", "Tolkien", "Asimov"]

І знову тип виводиться автоматично: елементи — рядки, отже масив [String].

Виведення типів для масивів

Виведення типу масиву працює просто: Swift дивиться на елементи в квадратних дужках і намагається зрозуміти, якого вони типу. Якщо всі елементи одного типу — чудово, тип масиву визначено.

Складність починається, коли елементи здаються схожими, але насправді мають різні типи. Наприклад, 1 — це Int, а 1.5 — уже Double. В одному масиві тримати їх разом не можна, бо масив має містити елементи одного типу.

Рішення: зробити масив Double, якщо перетворити цілі числа на Double.

let measurements: [Double] = [1.0, 1.5, Double(2)]
print(measurements) // [1.0, 1.5, 2.0]

Тут ми явно підказали тип масиву ([Double]), і Swift погодився. Якби ми написали [1, 1.5, 2], компілятор би зупинив нас і сказав: «визначтеся, що ви хочете зберігати». І це добре: компілятор не дає зробити програму «майже робочою», але з сюрпризом під час виконання.

Невелика схема того, як зазвичай працює виведення типу:

flowchart TD
    A["Пишемо літерал масиву: [ ... ]"] --> B["Swift дивиться на елементи"]
    B --> C{"Усі елементи одного типу?"}
    C -->|Так| D["Тип масиву виводиться автоматично"]
    C -->|Ні| E["Потрібна підказка: явний тип або перетворення"]

Порожній масив і чому [] недостатньо

Найчастіший сюрприз для новачків — ви хочете почати зі списку, у якому поки що нічого немає. Логіка проста: пишемо []. Але Swift не телепат (принаймні, офіційно), і йому потрібно розуміти, які елементи будуть у масиві.

Так робити не можна (не компілюється):

  • let items = [] — компілятор не знає, що це за елементи.

Правильний спосіб — явно вказати тип. Є кілька рівнозначних варіантів.

Варіант 1: вказати тип ліворуч:

let books: [String] = []
print(books) // []

Варіант 2: використати конструктор без аргументів для типу масиву:

let ids = [Int]()
print(ids) // []

Варіант 3: через форму Array<T>():

let flags = Array<Bool>()
print(flags) // []

Усі три варіанти створюють порожній масив. Відрізняється лише те, як саме ви пояснюєте Swift, що зберігатиметься всередині.

Два записи типу масиву: [Int] і Array<Int>

У Swift є дві рівноправні форми запису типу масиву. Коротка й найпопулярніша — [Int], [String], [Bool]. Довша й більш загальна — Array<Int>, Array<String>. Це один і той самий тип, просто різний запис.

Корисно розуміти обидві форми, бо в документації та в сигнатурах ви часто зустрінете Array<Element>. Загалом Array — це узагальнений (generic) тип, і запис Array<String> якраз показує: «усередині лежить String».

Приклади, які компілюються однаково:

let a: [Int] = [1, 2, 3]
let b: Array<Int> = [1, 2, 3]

print(a) // [1, 2, 3]
print(b) // [1, 2, 3]

Іноді довга форма допомагає не губитися в коді, коли тип стає складнішим. Але на базовому рівні найчастіше достатньо короткої форми [T].

let vs var під час створення масиву

З масивами працює те саме правило, що і з будь-якими значеннями в Swift.

Якщо ви оголосили масив через let, отримуєте константу: не можна змінювати ані сам масив, ані його склад, ані порядок. Якщо через var — масив можна змінювати.

Сьогодні ми ще не користуємося методами append, insert і remove (це буде трохи пізніше), тому різницю відчуємо через просту річ — переприсвоєння.

var tags = ["swift", "basics"]
print(tags) // ["swift", "basics"]

tags = ["swift", "basics", "arrays"]
print(tags) // ["swift", "basics", "arrays"]

А тепер константа:

let roles = ["admin", "editor"]
print(roles) // ["admin", "editor"]

roles можна друкувати, передавати у функції, читати, але не можна змінювати.

Інтуїтивна аналогія: let — це «список, який ви роздрукували та заламінували». Читати можна, дописати маркером уже не вийде. var — це «список у нотатках», який можна редагувати.

Мініпроєкт: заготовка ReadingList

Щоб приклади не виглядали набором випадкових чисел, почнемо мініпроєкт цього дня — консольну заготовку «ReadingList». Поки що вона максимально проста: зберігає список книг як масив рядків і друкує його на екран.

import Foundation

let readingList = [
    "The Left Hand of Darkness",
    "The Hobbit",
    "Foundation"
]

print("Мій список читання:")      // Мій список читання:
print(readingList)               // ["The Left Hand of Darkness", "The Hobbit", "Foundation"]

Тут import Foundation не обов’язковий для масивів, але в консольних проєктах його часто залишають зі звички: далі зазвичай ідуть рядки, введення, форматування та інші зручності. Якщо хочете, можете прибрати — приклад усе одно скомпілюється.

Масиви різних типів: [Int], [String], [Bool]

Коли ви тільки починаєте, може здаватися, що тип елементів — це «формальність». Але для Swift це основа. Тип визначає, які операції доступні і що взагалі вважається коректним.

Наприклад, масив чисел можна використовувати для статистики, а масив рядків — для виведення повідомлень і обробки тексту. Swift не дасть вам випадково змішати рядок і число в одному масиві без явного перетворення — і це економить години життя, зазвичай ті, які ви витратили б на пошук дурного бага о другій ночі.

let attempts: [Int] = [1, 2, 3, 4]
let names: [String] = ["Ana", "Ben"]
let isCompleted: [Bool] = [true, false, false]

print(attempts)      // [1, 2, 3, 4]
print(names)         // ["Ana", "Ben"]
print(isCompleted)   // [true, false, false]

Масив масивів

Часте питання: «Чи можна зробити масив, де елементи — теж масиви?» Технічно так: тип матиме вигляд [[Int]] або [[String]].

Поки що без індексів і без «матриць» — просто один рядок, щоб сама ідея не виглядала магією:

let shelves: [[String]] = [["A", "B"], ["C"]]
print(shelves) // [["A", "B"], ["C"]]

Запам’ятайте головне: якщо елемент масиву — це інший масив, тип стає «подвійними дужками».

Виведення масиву через print() як інструмент налагодження

На старті програмування важливо мати «ліхтарик», яким ви підсвічуєте те, що справді відбувається в програмі. print() — це ваш ліхтарик. І з масивами він працює зручно: Swift друкує масив у читабельному вигляді, з квадратними дужками та елементами через кому.

Подивімося, як це допомагає бачити порядок:

let queue = ["first", "second", "third"]
print(queue) // ["first", "second", "third"]

Навіть без індексів уже видно: порядок збережено. Це знадобиться пізніше, коли ви почнете додавати та видаляти елементи, і print(queue) швидко покаже, як змінився вміст.

4. Ще кілька корисних нюансів

Іноді масив створюється не лише літералом. Але поки тримаємося простих і читабельних варіантів, без переходу до нових тем.

Створити порожній масив і одразу надрукувати:

var notes: [String] = []
print(notes) // []

Створити масив з одного елемента (так, це нормально):

let single = ["only one"]
print(single) // ["only one"]

Створити масив і дати йому змістовну назву — це не частина синтаксису, але важлива звичка:

let errorMessages = ["Invalid input", "Try again"]
print(errorMessages) // ["Invalid input", "Try again"]

Таблиця-шпаргалка зі створення масивів

Іноді корисно побачити все в одному місці, особливо якщо ви тільки звикаєте до синтаксису. Нижче — невелика шпаргалка саме зі створення, без індексів і мутацій.

Що хочемо зробити Як пишемо Коментар
Створити масив чисел
let a = [1, 2, 3]
Тип [Int] буде виведено автоматично
Створити масив рядків
let s = ["a", "b"]
Тип [String] буде виведено автоматично
Створити порожній масив рядків
let x: [String] = []
Без типу [] незрозуміло, що це за масив
Створити порожній масив Int
var ids = [Int]()
Зручно, якщо додаватимете елементи пізніше
Вказати тип через Array<T>
let a: Array<Int> = [1]
Еквівалент [Int], просто інший запис

5. Типові помилки під час створення масивів

Помилка №1: писати let a = [] і чекати, що Swift здогадається про тип.
[] — це «нічого», і з цього неможливо зрозуміти, що ви планували зберігати: рядки, числа чи, можливо, назви котиків. Виправлення просте: або вкажіть тип ліворуч (let a: [Int] = []), або створіть через конструктор (let a = [Int]()).

Помилка №2: намагатися змішувати в одному масиві елементи різних типів.
Дуже частий випадок — «хочу [1, 2, 2.5]». Для людини це «числа», а для Swift — Int і Double, тобто різні типи. Правильний шлях: визначити потрібний тип (часто Double) і явно перетворити елементи: [1.0, 2.0, 2.5] або Double(1).

Помилка №3: плутати масив і кортеж через дужки.
[1, 2, 3] — масив, (1, 2, 3) — кортеж. У масиву елементи одного типу, і їх можна зберігати скільки завгодно. У кортежу фіксована кількість елементів, і типи можуть бути різними. Якщо ви випадково написали круглі дужки, код або не скомпілюється там, де ви очікували масив, або поводитиметься не так, як задумано.

Помилка №4: оголосити масив через let, а потім очікувати, що він розширюватиметься.
Навіть якщо вам здається логічним, що «назва списку не змінюється, а елементи додаватимуться», це все одно означає мутацію масиву. Для змін складу потрібен var. Ми докладно змінюватимемо масиви в наступних лекціях, але звичку думати про let/var заздалегідь варто формувати вже зараз.

Помилка №5: давати масиву неінформативну назву й потім плутатися, що в ньому.
let a = ["Tom", "Ben"] компілюється, але за тиждень ви забудете, що це: автори, користувачі, теги? Вдалі назви (authors, usernames, tags) допомагають читати код як текст — і це особливо важливо, коли масиви почнуть мандрувати функціями та умовами.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ