Kotlin Курс

Коллекции Kotlin: List, Map, Set

Самоучитель Python 3, собранный из материалов данного сайта.Предназначен в основном для тех, кто хочет изучить язык программирования Python с нуля.

Урок 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
Порядок Есть (по индексу) Нет (но может быть) Нет (но может быть)
Уникальность Не обязательно Обязательно Ключи уникальны
Доступ по индексу Да Нет Нет (по ключу)
Пример Список покупок Коллекция марок Телефонная книга

Практические советы

  1. Выбирай правильную коллекцию: если нужен порядок и возможны дубликаты – List. Если нужна уникальность – Set. Если нужен быстрый поиск по ключу – Map.
  2. Не бойся изменяемых коллекций: mutableListOf, mutableSetOf, mutableMapOf – они гибкие. Но если коллекция не должна меняться, используй неизменяемые версии (listOf, setOf, mapOf) – это делает код безопаснее.
  3. Используй цикл for: он проще и понятнее, чем работа с индексами.
  4. Помни про 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. Уникальные имена (среднее)

Напиши программу, которая:

  1. Создаёт изменяемое множество имён (mutableSetOf).
  2. Добавляет 5 разных имён.
  3. Пытается добавить ещё одно имя, которое уже есть в множестве.
  4. Выводит размер множества (должен быть 5, а не 6).
  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 и другие.

Коллекции – это основа любого приложения. Без них невозможно представить ни игру, ни сайт, ни приложение для заметок. Потренируйся на заданиях, и ты почувствуешь себя настоящим программистом!

В следующем уроке мы узнаем, как работать с функциями – создавать свои собственные команды, которые можно вызывать много раз. А пока – удачи с заданиями! 🚀