Урок 6: Коллекции в Kotlin – как хранить много вещей сразу
Введение
Представь, что ты собираешься в поход. Ты не можешь взять с собой только одну вещь – тебе нужен рюкзак, чтобы сложить туда еду, воду, компас, карту и спальный мешок. В программировании такой «рюкзак» называется коллекцией. Коллекции позволяют хранить не одно значение, а целую группу значений. Сегодня мы научимся пользоваться тремя главными «рюкзаками» в Kotlin: списком (List), множеством (Set) и словарём (Map).
Зачем это нужно? Допустим, ты пишешь игру. Тебе нужно хранить список всех игроков, их очки, оружие в инвентаре. Или ты делаешь приложение для заметок – там тоже много записей. Без коллекций пришлось бы создавать сотни переменных: игрок1, игрок2, игрок3… Это неудобно. Коллекции решают эту проблему: ты создаёшь одну переменную, а в ней – сколько угодно элементов.
💡 Аналогия из жизни: Список (List) – это как список покупок в телефоне: ты можешь добавлять новые пункты, удалять ненужные, смотреть, что уже купил. Множество (Set) – это как коробка с разноцветными шариками, где каждый цвет встречается только один раз. Словарь (Map) – это как телефонная книга: у каждого имени (ключа) есть свой номер телефона (значение).
1. Список (List) – упорядоченный набор элементов
Список – самая популярная коллекция. В ней элементы идут по порядку, как вагоны поезда. У каждого элемента есть свой номер – индекс. Первый элемент имеет индекс 0, второй – 1 и так далее. В Kotlin есть два вида списков:
- listOf – неизменяемый список. Его нельзя изменить после создания.
- mutableListOf – изменяемый список. Можно добавлять, удалять и менять элементы.
Создаём список
// Неизменяемый список (только для чтения)
val fruits = listOf("яблоко", "банан", "апельсин")
println(fruits) // [яблоко, банан, апельсин]
// Изменяемый список
val mutableFruits = mutableListOf("яблоко", "банан")
mutableFruits.add("груша") // добавляем элемент
mutableFruits.add("киви")
println(mutableFruits) // [яблоко, банан, груша, киви]
// Можно создавать пустой список
val emptyList = mutableListOf<String>() // список строк, пока пустой
emptyList.add("первый элемент")
println(emptyList) // [первый элемент]
Как обращаться к элементам?
У каждого элемента есть индекс. Чтобы получить элемент, пишем имя списка и в квадратных скобках индекс:
val fruits = listOf("яблоко", "банан", "апельсин")
println(fruits[0]) // яблоко (индекс 0 – первый элемент)
println(fruits[2]) // апельсин (индекс 2 – третий элемент)
// Можно использовать метод get()
println(fruits.get(1)) // банан
Важно: если попросить элемент с несуществующим индексом, программа упадёт с ошибкой. Например, fruits[10] вызовет исключение. Всегда проверяй размер списка:
if (fruits.size > 10) {
println(fruits[10])
} else {
println("В списке всего ${fruits.size} элементов, индекс 10 недоступен")
}
Добавляем, удаляем, изменяем
В изменяемом списке можно делать всё что угодно:
val shoppingList = mutableListOf("молоко", "хлеб", "яйца")
println("Начальный список: $shoppingList")
// Добавляем один элемент
shoppingList.add("сыр")
println("После добавления сыра: $shoppingList")
// Добавляем сразу несколько
shoppingList.addAll(listOf("масло", "колбаса"))
println("После добавления группы: $shoppingList")
// Удаляем по значению
shoppingList.remove("хлеб")
println("После удаления хлеба: $shoppingList")
// Удаляем по индексу
shoppingList.removeAt(0) // удаляем первый элемент (молоко)
println("После удаления первого: $shoppingList")
// Изменяем элемент по индексу
shoppingList[1] = "творог" // меняем второй элемент на "творог"
println("После замены: $shoppingList")
// Очищаем весь список
shoppingList.clear()
println("После очистки: $shoppingList")
Полезные методы для списков
val numbers = mutableListOf(10, 20, 30, 40, 50, 20)
println("Размер списка: ${numbers.size}") // 6
println("Содержит ли 30? ${numbers.contains(30)}") // true
println("Индекс числа 20: ${numbers.indexOf(20)}") // 0 (первое вхождение)
println("Последний индекс 20: ${numbers.lastIndexOf(20)}") // 4
println("Пустой ли список? ${numbers.isEmpty()}") // false
// Переворачиваем список
val reversed = numbers.reversed()
println("Обратный порядок: $reversed")
// Сортировка
val sorted = numbers.sorted()
println("По возрастанию: $sorted")
// Сортировка по убыванию
val sortedDesc = numbers.sortedDescending()
println("По убыванию: $sortedDesc")
Перебор списка (цикл for)
Часто нужно пройтись по всем элементам и что-то с ними сделать. Для этого используем цикл for:
val fruits = listOf("яблоко", "банан", "апельсин", "киви")
// Простой перебор
for (fruit in fruits) {
println("Фрукт: $fruit")
}
// Вывод:
// Фрукт: яблоко
// Фрукт: банан
// Фрукт: апельсин
// Фрукт: киви
// Если нужны и индекс, и значение
for ((index, fruit) in fruits.withIndex()) {
println("$index: $fruit")
}
// Вывод:
// 0: яблоко
// 1: банан
// 2: апельсин
// 3: киви
Цикл for – твой лучший друг при работе с коллекциями. Он автоматически проходит по каждому элементу, тебе не нужно думать об индексах.
2. Множество (Set) – только уникальные значения
Множество похоже на список, но с одним важным отличием: все элементы уникальны. Если попытаться добавить дубликат, он просто не добавится. Порядок элементов не гарантирован (хотя в Kotlin есть упорядоченные версии).
Представь, что ты коллекционируешь марки. У тебя не может быть двух одинаковых марок в альбоме – это же бессмысленно. Множество работает так же.
// Неизменяемое множество
val uniqueNumbers = setOf(1, 2, 3, 3, 2, 1)
println(uniqueNumbers) // [1, 2, 3] – дубликаты отброшены
// Изменяемое множество
val mutableSet = mutableSetOf("кот", "собака", "попугай")
mutableSet.add("хомяк")
mutableSet.add("кот") // не добавится, так как "кот" уже есть
println(mutableSet) // [кот, собака, попугай, хомяк]
// Проверка на наличие
if (mutableSet.contains("собака")) {
println("Собака есть в множестве")
}
// Удаление
mutableSet.remove("попугай")
println(mutableSet) // [кот, собака, хомяк]
Где использовать множество? Например, для хранения списка имён пользователей, которые уже голосовали. Или для хранения уникальных ID товаров в корзине.
3. Словарь (Map) – пары «ключ-значение»
Словарь – это как шкафчик с ячейками. У каждой ячейки есть номер (ключ), и в ней лежит вещь (значение). Ключи уникальны, а значения могут повторяться. В реальной жизни это телефонная книга (имя → номер), словарь (слово → перевод), меню (название блюда → цена).
// Неизменяемый словарь
val phoneBook = mapOf(
"Мама" to "8-999-123-45-67",
"Папа" to "8-999-765-43-21",
"Друг" to "8-999-555-55-55"
)
println(phoneBook) // {Мама=8-999-123-45-67, Папа=8-999-765-43-21, Друг=8-999-555-55-55}
// Получаем значение по ключу
println(phoneBook["Мама"]) // 8-999-123-45-67
println(phoneBook.get("Папа")) // 8-999-765-43-21
// Если ключа нет, вернётся null
println(phoneBook["Бабуля"]) // null
// Безопасное получение с значением по умолчанию
println(phoneBook.getOrDefault("Бабуля", "номер не найден")) // номер не найден
Изменяемый словарь
val scores = mutableMapOf(
"Иван" to 95,
"Мария" to 88,
"Петр" to 76
)
// Добавляем новую пару
scores["Анна"] = 92
println(scores) // {Иван=95, Мария=88, Петр=76, Анна=92}
// Изменяем значение по ключу
scores["Петр"] = 80
println(scores) // {Иван=95, Мария=88, Петр=80, Анна=92}
// Удаляем по ключу
scores.remove("Мария")
println(scores) // {Иван=95, Петр=80, Анна=92}
// Проверяем, есть ли ключ
if (scores.containsKey("Иван")) {
println("Иван есть в списке")
}
// Получаем все ключи и значения
println("Ключи: ${scores.keys}") // [Иван, Петр, Анна]
println("Значения: ${scores.values}") // [95, 80, 92]
Перебор словаря
val menu = mapOf(
"Пицца" to 450,
"Бургер" to 350,
"Салат" to 250,
"Сок" to 150
)
// Перебор пар
for ((блюдо, цена) in menu) {
println("$блюдо стоит $цена рублей")
}
// Только ключи
for (блюдо in menu.keys) {
println("В меню есть: $блюдо")
}
// Только значения
for (цена in menu.values) {
println("Цена: $цена")
}
Сравнение коллекций
| Свойство | List | Set | Map |
|---|---|---|---|
| Порядок | Есть (по индексу) | Нет (но может быть) | Нет (но может быть) |
| Уникальность | Не обязательно | Обязательно | Ключи уникальны |
| Доступ по индексу | Да | Нет | Нет (по ключу) |
| Пример | Список покупок | Коллекция марок | Телефонная книга |
Практические советы
- Выбирай правильную коллекцию: если нужен порядок и возможны дубликаты – List. Если нужна уникальность – Set. Если нужен быстрый поиск по ключу – Map.
- Не бойся изменяемых коллекций: mutableListOf, mutableSetOf, mutableMapOf – они гибкие. Но если коллекция не должна меняться, используй неизменяемые версии (listOf, setOf, mapOf) – это делает код безопаснее.
- Используй цикл for: он проще и понятнее, чем работа с индексами.
- Помни про null: при получении элемента по индексу или ключу может вернуться null. Используй
getOrDefaultили проверяй на null.
Пример из реальной игры
Представь, что мы пишем простую RPG-игру. У игрока есть инвентарь (список предметов), уникальные достижения (множество) и статистика (словарь).
fun main() {
// Инвентарь – список предметов (могут повторяться)
val inventory = mutableListOf("меч", "щит", "зелье здоровья", "меч")
println("Инвентарь: $inventory")
// Добавляем предмет
inventory.add("золото")
println("Подобрали золото: $inventory")
// Используем зелье – удаляем его
inventory.remove("зелье здоровья")
println("Выпили зелье: $inventory")
// Достижения – множество (каждое уникально)
val achievements = mutableSetOf("победил гоблина", "нашел сундук")
achievements.add("победил дракона")
achievements.add("победил гоблина") // не добавится, уже есть
println("Достижения: $achievements")
// Статистика – словарь (название -> значение)
val stats = mutableMapOf(
"уровень" to 5,
"здоровье" to 100,
"мана" to 50
)
println("Статистика: $stats")
// Повышаем уровень
stats["уровень"] = 6
println("После повышения: $stats")
// Выводим всё красиво
println("\n=== ИТОГО ===")
println("В инвентаре ${inventory.size} предметов")
println("Получено ${achievements.size} достижений")
println("Уровень персонажа: ${stats["уровень"]}")
}
Практические задания
А теперь попробуй сам! Вот три задания разной сложности. Пиши код в своей среде разработки (IntelliJ IDEA или на сайте-компиляторе Kotlin).
Задание 1. Список дел (лёгкое)
Создай изменяемый список дел на день. Добавь в него 5 дел (например, "проснуться", "позавтракать", "сделать уроки", "погулять", "почитать книгу"). Затем:
- Выведи весь список.
- Удали второе дело (по индексу).
- Добавь новое дело в конец списка.
- Замени первое дело на "встать с кровати".
- Выведи итоговый список.
💡 Подсказка: используйmutableListOf, методыadd,removeAt,set(илисписок[индекс] = значение).
Задание 2. Уникальные имена (среднее)
Напиши программу, которая:
- Создаёт изменяемое множество имён (
mutableSetOf). - Добавляет 5 разных имён.
- Пытается добавить ещё одно имя, которое уже есть в множестве.
- Выводит размер множества (должен быть 5, а не 6).
- Проверяет, есть ли в множестве имя "Анна". Если есть – выведи "Анна в списке", иначе – "Анны нет".
- Удали одно имя и снова выведи размер.
💡 Подсказка: используйadd,contains,remove,size.
Задание 3. Телефонная книга (сложное)
Создай изменяемый словарь (mutableMapOf), который хранит имена и номера телефонов. Добавь 3 контакта (например, "Иван" -> "111-111", "Мария" -> "222-222", "Петр" -> "333-333"). Затем:
- Выведи номер Марии.
- Измени номер Петра на "333-000".
- Добавь новый контакт "Анна" с номером "444-444".
- Удали контакт "Иван".
- Выведи все имена (ключи) и все номера (значения).
- Выведи весь словарь в формате: "Имя: номер".
💡 Подсказка: используй for ((key, value) in phoneBook) для красивого вывода.
Заключение
Сегодня мы познакомились с тремя главными коллекциями Kotlin: List (список), Set (множество) и Map (словарь). Ты научился:
- Создавать изменяемые и неизменяемые коллекции.
- Добавлять, удалять и изменять элементы.
- Перебирать коллекции с помощью цикла
for. - Использовать полезные методы:
size,contains,indexOf,sortedи другие.
Коллекции – это основа любого приложения. Без них невозможно представить ни игру, ни сайт, ни приложение для заметок. Потренируйся на заданиях, и ты почувствуешь себя настоящим программистом!
В следующем уроке мы узнаем, как работать с функциями – создавать свои собственные команды, которые можно вызывать много раз. А пока – удачи с заданиями! 🚀