1. Навіщо потрібні масиви
Коли ви лише починаєте програмувати, може здаватися, що змінних можна плодити нескінченно: score1, score2, score3… — і все буде нормально. Але реальність швидко нагадує: програміст — не бухгалтер із калькулятором. Зберігати «список однотипних речей» у десятках окремих змінних — прямий шлях до хаосу, помилок і розчарування.
Уявіть завдання: зберігати кроки за тиждень, або оцінки за 10 контрольних, або температуру за днями. Якщо робити це окремими змінними, ви одразу стикаєтеся з трьома проблемами: незручно перебирати значення циклом, незручно передавати «набір» у функцію, незручно розширювати логіку. Потрібна структура, яка тримає «багато однотипних за змістом даних» під одним іменем. Такою структурою і є масив.
У програмуванні корисно дивитися на речі під різними кутами. Коли ви бачите 7 значень (наприклад, за днями тижня), можна думати так: «це сім окремих чисел». А можна інакше: «це один набір даних, у якого є розмір і елементи». Масив якраз і привчає сприймати такі значення як набір однотипних речей.
Уявімо, як виглядав би трекер тренувань без масивів. Це було б щось на кшталт:
fun main() {
val mon = 0
val tue = 30
val wed = 0
val thu = 45
val fri = 0
val sat = 60
val sun = 15
println("Monday minutes = $mon") // Хвилини в понеділок = 0
}
Працює? Так. Зручно? Уже не дуже. А якщо потім потрібно «пройтися циклом за днями» і вивести всі значення? А якщо треба знайти максимум? А якщо потрібно замінити «7 днів» на «30 днів» (умовно)? Ось тут масиви й починають вигравати з розгромним рахунком.
Що таке масив
Масив — це структура даних, яка зберігає фіксовану кількість значень одного типу (або його підтипів). У Kotlin масиви — цілком офіційний інструмент, щоб зберігати багато однотипних значень і працювати з ними як з єдиним обʼєктом. Найважливіше слово тут — фіксовану: скільки елементів задали спочатку, стільки й буде.
Зручна побутова аналогія: масив — це коробка з пронумерованими комірками. Коробку вже зроблено на заводі, і кількість комірок не змінюється. Ви можете перекладати вміст комірок, можете дивитися, що лежить у конкретній комірці, можете пройтися по всіх комірках по порядку — але «вставити ще одну комірку між 3 і 4» не можна. «Коробка» не підтримує розширення.
Слово «фіксований» варто підкреслити ще раз, бо саме тут початківці найчастіше дивуються. Масив не можна «розширити» так, як розширюють список справ на папері: дописали пункт — і готово. Якщо в масиві 7 елементів, то «восьмий» туди не поміститься. Це фундаментальна властивість масивів: розмір задається під час створення й не змінюється.
На практиці це означає таке: якщо вам раптом знадобилося більше місця, ви створюєте новий масив і переносите дані. Звучить трохи лячно, але згодом ми навчимося робити це зручніше. Сьогодні достатньо запамʼятати правило: масив — це «фіксована коробка», а не «гумовий пакет».
2. Види масивів у Kotlin: Array і IntArray
У Kotlin є дві родини масивів, про які варто знати ще на старті:
- Array<T> — універсальний масив для обʼєктів (наприклад, String і будь-яких класів).
- примітивні масиви (IntArray, DoubleArray, CharArray тощо) — оптимізовані для чисел і символів, без зайвих накладних витрат.
Array<T>: універсальний масив
Головний «універсальний» масив Kotlin — це Array<T>. Він підходить для зберігання обʼєктів: рядків, будь-яких класів (а згодом, коли зʼявляться власні типи, — і їх також). Тип елемента пишеться в кутових дужках: Array<String>, Array<Int>, Array<Char>.
Найпростіший спосіб створити такий масив — arrayOf(...): ви перелічуєте елементи, а Kotlin збирає з них масив.
Приклад: масив рядків через arrayOf(...)
fun main() {
val names: Array<String> = arrayOf("Ann", "Bob", "Cindy")
println("names size = ${names.size}") // names size = 3
}
Тут одразу зʼявляється нова важлива властивість: size. Це кількість елементів масиву. Вона потрібна відразу, бо всі межі масиву залежать від його розміру.
Приклад: Kotlin часто сам виводить тип
fun main() {
val colors = arrayOf("red", "green", "blue")
println("colors size = ${colors.size}") // colors size = 3
}
Ми не написали Array<String> — і все одно вийшло правильно, тому що Kotlin вивів тип за значеннями. Це зручно, але на перших порах корисно іноді вказувати тип явно, щоб мозок звикав читати код «зліва направо».
Примітивні масиви: IntArray, DoubleArray, CharArray
У цьому місці майже всі початківці ставлять логічне запитання: «Чому не можна завжди використовувати Array<T>? Навіщо тоді ще якісь IntArray, DoubleArray?»
Відповідь така: використовувати Array<T> можна, але в Kotlin спеціально додали окремі масиви для примітивних типів: IntArray, DoubleArray, CharArray тощо. Мова Java, на основі якої створювали Kotlin, дає змогу дуже компактно пакувати примітивні типи в масиви.
Елементи в примітивних масивах упаковані щільніше, ніж у Array<T>. Завдяки цій оптимізації вони займають менше місця й працюють швидше. Запамʼятайте просте правило: для цілих чисел у базових завданнях обирайте IntArray, для Double — DoubleArray, для символів — CharArray. Так код легше читати, і буде менше несподіванок. Kotlin прямо виділяє такі «примітивні масиви» як окремі типи.
Порівняймо без прикрас:
| Що зберігаємо | Універсальний масив | Примітивний масив |
|---|---|---|
| Рядки | |
(немає окремого) |
| Цілі числа | |
|
| Дробові | |
|
| Символи | |
|
Важливо: IntArray — це не Array<Int>. Вони схожі у використанні (є size, можна перебирати циклом), але це різні типи.
Приклад: масив цілих через intArrayOf(...)
fun main() {
val scores: IntArray = intArrayOf(10, 20, 30)
println("scores size = ${scores.size}") // scores size = 3
}
3. Створення масиву потрібного розміру та значення за замовчуванням
Досі ми створювали масиви тоді, коли вже знали конкретні елементи. Але в реальних завданнях часто навпаки: ми знаємо скільки елементів потрібно, а значення зʼявляться пізніше (наприклад, користувач введе 7 чисел — по одному на кожен день тижня). Для цього в примітивних масивів є конструктор вигляду IntArray(size).
І тут важливий момент: масив буде заповнений значеннями за замовчуванням. Для Int це 0.
Приклад: створюємо масив на 5 елементів, заповнений нулями
fun main() {
val zeros = IntArray(5)
println("zeros size = ${zeros.size}") // zeros size = 5
for (x in zeros) {
print("$x ")
}
// 0 0 0 0 0
}
Зверніть увагу на for (x in zeros): ми перебираємо елементи масиву «за значеннями». Поки що ми не працюємо з індексами (це буде наступна лекція), але вже можемо побачити вміст.
5. Мінізастосунок: трекер тренувань за тиждень
Щоб масиви були не «у вакуумі», почнемо збирати маленький консольний застосунок, який будемо розвивати далі за курсом. Ідея проста: ми хочемо зберігати дані про тренування за тиждень. Поки — без введення, без редагування, без статистики: лише заготовка структури. Це дуже схоже на реальну розробку: спочатку робимо каркас, а потім наповнюємо його логікою.
У першій версії ми заведемо:
- масив назв днів тижня (рядки),
- масив хвилин тренування для кожного дня (цілі числа),
- виведемо, скільки в нас днів у даних, щоб переконатися, що структуру зібрано.
Приклад: каркас даних (дні + хвилини)
fun main() {
val days: Array<String> = arrayOf("Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun")
val minutes: IntArray = intArrayOf(0, 30, 0, 45, 0, 60, 15)
println("Days count = ${days.size}") // Days count = 7
println("Minutes count = ${minutes.size}") // Minutes count = 7
}
Поки це виглядає як «просто два масиви», але тут уже видно важливу думку: дані однієї природи варто зберігати разом. Ми не робимо monMinutes, tueMinutes, wedMinutes — ми робимо один масив minutes, у якому кожна позиція відповідає певному дню тижня.
І ще один момент: якщо у вас два масиви, які «йдуть паралельно» (дні та хвилини), їхні розміри мають збігатися. Інакше логіка почне ламатися, а налагодження перетвориться на археологічну експедицію вашим кодом.
6. Типові помилки під час роботи з масивами
Помилка № 1: очікувати, що масив «сам розшириться».
Початківці інколи думають: «ну я ж можу присвоїти a[10] = 5, значить масив стане більшим». Ні: розмір фіксований, а спроба звернутися за межі призведе до помилки під час виконання. Запамʼятайте як правило: масив — це коробка фіксованого розміру.
Помилка № 2: плутати Array<Int> і IntArray та дивуватися, чому Kotlin не приймає одне замість іншого.
Це різні типи. У базових завданнях із цілими числами обирайте IntArray: так код простіше читати, і менше шансів заплутатися. Kotlin окремо описує примітивні масиви як самостійні класи.
Помилка № 3: створити IntArray(n) і чекати «випадкові значення» або «порожнечу».
IntArray(n) заповнюється нулями, тому що Kotlin гарантує передбачуваний початковий стан. Це зручно, але якщо ви очікували іншого — обовʼязково зробіть окремий крок заповнення (ми займатимемося цим у наступних лекціях).
Помилка № 4: робити масив із «усього підряд», змішуючи типи.
Масив любить порядок: один масив — один тип. Якщо вам здається, що «дуже хочеться змішати», то найчастіше це сигнал, що дані потрібно зберігати інакше (але це вже розмова не на сьогодні). Зараз тримаємося простого правила: Array<String> для рядків, IntArray для цілих.
Помилка № 5: забувати перевіряти здоровий глузд розмірів паралельних масивів.
Якщо у вас два масиви, які мають бути синхронними (наприклад, days і minutes), вони зобовʼязані мати однаковий size. Інакше ви потім отримаєте «день є, хвилин немає» — майже як абонемент у спортзал, але без спортзалу.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ