Урок 9: Списки в Kotlin, функции для работы с коллекциями

списки List - Android [Kotlin] для начинающих

Коллекции

По определению коллекции – это группы с переменным количеством элементов (или нулем элементов). Объекты внутри как правило имеют единый тип. Коллекции называются коллекциями, потому что корнем их иерархии в языке является класс Collection<T>. Позже, при изучении наследования это станет понятнее, пока не будем сильно углубляться.

Типы коллекций

  • List (список) – упорядоченный набор элементов, к ним можно обращаться по индексам. В списке могут встречаться дубликаты.
  • Set (множество) – коллекция уникальных элементов. То есть повторяющиеся элементы исключены. Порядок элементов может быть любым.
  • Map (словарь или ассоциативным список) – определенный набор пар. Пара содержит в себе ключ и значение. Зная ключ, можно обратиться к какому-нибудь значению. Ключи строго уникальны, а значения могут иметь дубликаты.

Списки

В этот раз подробнее остановимся на списках. Поведение напоминает работу с массивами, однако, есть существенные отличия в принципе работы.

Отличия от массивов

  • Первое. Массив имеет строго фиксированный размер и не может уменьшаться или увеличиваться. Изменить размер массива можно только создав его копию с дополнительными или утраченными элементами. Списки же имеют методы add и remove для добавления/удаления элементов.
  • Второе. Массив предоставляется классом Array<T>. List<T> является интерфейсом и имеет разные реализации со своим функционалом.
  • Третье. Массивы оптимизированы для примитивов и меют отдельные типа IntArray, CharArray и т.д. Списки такой оптимизации не имеют.
  • Четвертое. Различается процесс сравнивания элементов друг с другом. В массивах сравниваются адреса ячеек в памяти, в списках же идет сравнение самого значения.

В целом хорошей практикой считается использовать списки вместо массивов везде, где это позволяет техническая реализация. Однако, с точки зрения производительности, массивы имеют над списками преимущество.

Создание списков в Котлин

Списки создаются с помощью функции listOf() из стандартной библиотеки Kotlin. Короткая запись создания целочисленного списка выглядит так:

val list = listOf(4, 4, 2)

Если принудительно проставить тип списка, получим List. Это базовый интерфейс для всех неизменяемых списков:

val list: List<Int> = listOf(4, 4, 2)

Его “неизменяемость” определяется тем, что нельзя просто так взять и обратиться по индексу к элементу и заменить его на другой. Как мы делали это с массивами. Также отсутствуют функции по добавлению и удалению элементов.

Изменяемые списки в Kotlin

В таких списках мы можем читать конкретное значение по индексу, а также пробегать циклом по всем значениям вместе. Но для того, чтобы создать изменяемый список, необходимо использовать функцию mutableListOf().

При данной инициализации тип переменной будет MutableList<T>. Это второй базовый интерфейс для списков, на основе которого создаются изменяемые списки. Это List с возможностью добавления или удаления элементов:

val list3: MutableList<Int> = mutableListOf(1, 2, 3)

Функции для работы со списками

Теперь переходим к инструментарию по работе со списками. В уроке примеры будут представлены на примере целочисленного списка. А в практике, прилагаемой к уроку, будут оформлены задачи с применением различных типов с неким прикладным контекстом.

Еще раз создадим изменяемый список с некоторым количеством чисел.

val mutableList = mutableListOf(11, 15, 20, 12, 9, 14)
Функция add()

Сначала будем добавлять значения в список с помощью функции add(). Чтобы воспользоваться ей, необходимо написать переменную со списком и вызвать для нее этот метод с помощью обращения через точку.

Метод add() может принимать два набора параметров и в зависимости от этого, будет произведены разные действия.

Первый вариант – это всего один параметр, который является новым элементом, который нужно добавить в список. Этот элемент по умолчанию добавляется в конец списка. Результат в консоли:

val mutableList = mutableListOf(11, 15, 20, 12, 9, 14)
println(mutableList)

mutableList.add(42)
println(mutableList)

Второй вариант – это когда метод add() принимает два параметра. На первом месте прописывается индекс – то место, куда должен быть вставлен элемент. Проставим ноль. На втором месте, через запятую, сам элемент. Функция, принимающая два параметра, выглядит так. Результат – число 42 добавлено в начало списка.

mutableList.add(0, 42)
println(mutableList)

Кстати, во время занесения параметров в функцию можно вывести подсказку о том, какой именно параметр сейчас нужно ввести. Это окошко не всегда появляется, но оно вызовется с помощью сочетаний клавиш ctrl + P или cmd + P на маке. Курсор при этом должен находиться внутри скобок, где вводятся параметры метода.

Функция contains()

Следующая функция проверит наличие элемента в списке и вернет Boolean значение true или false. По тому же принципу, как делали это с помощью in в уроке про диапазоны. Сейчас применим метод contains() к списку со значением 42 и получим true. Число 42 содержится в списке mutableList.

println(mutableList.contains(42))
Методы isEmpty() и isNotEmpty()

Следующие функции часто пригождаются на практике при создании условий, когда нужно проверять: содержит ли список хоть какой элемент или же он пуст. Это методы isEmpty() и isNotEmpty(). Соответственно методы возвращают значения true или false.

println(mutableList.isEmpty())
println(mutableList.isNotEmpty())

Следующие функции помогут узнать индекс какого-либо элемента, если таковой присутствует в списке. Вызываем метод indexOf() с параметром 42 и получаем индекс 0. Все так. Если одинаковых значений в списке несколько, будет возвращено будет первое, которое встретится. Чтобы узнать последний индекс встречающегося элемента воспользуемся функцией lastIndexOf():

println(mutableList.indexOf(42))
println(mutableList.lastIndexOf(42))
Функции sort() и reverse()

Чтобы отсортировать список есть удобные функции. sort() отсортирует по возрастанию и sortDescending() по убыванию соответственно. Кроме того можно воспользоваться функцией reverse() для изменения порядка элементов в списке на обратный. После применения этих трех методов элементы будут расположены по возрастанию.

mutableList.forEach {
	println(it)
}

Мы еще не изучали лямбды, но еще немного задержимся тут с точки зрения возможностей оформления. Автоматически задекларированную переменную it можно:

  • во-первых: явно прописать, вызвав контекстное меню и выбрав принудительную простановку сигнатуры лямбды,
  • во-вторых: задать произвольное имя переменной, которая будет использоваться внутри тела лямбды. Часто этот прием нужен для повышения читабельности кода. Для этого it меняем на произвольное название и теперь эту переменную можно использовать внутри.

Ок. Очень похоже на принцип работы цикла for, не правда ли? Способ с forEach() выглядит более аккуратным и без дополнительных переменных из-за его стилистики функционального программирования. Но есть нюансы, связанные с удобностью и производительностью для списков или интервалов. Просто перечислю результаты наблюдений из которых можно выбрать собственный способ исходя из ситуации:

  • Если используется интервал, следует использовать цикл for.
  • Если используется коллекция, типа списка, то следует использовать forEach.
  • Если есть необходимость использовать операторы continue или break, то удобнее всего делать это в цикле for.

Расскажу еще про пару популярных функций, обязательных к изучению.

Функция filter()

Основная функция для фильтрации коллекций – filter(). Условие задается фигурных скобках, где указывается логическое выражение, которое возвращает true или false. Так как функция не изменяет список, ее можно использовать и с изменяемыми списками.

Напишем этот пример. Отфильтрованный список будет помещен в новую переменную. В фигурных скобках пишем условие. it (элемент коллекции) равен, например, 42. Ниже сделаем вывод нового списка через forEach(). Таким образом каждый элемент будет проверяться на равенство с числом 42 и если оно верно, элемент запишется в новый список.

val mutableList2 = mutableList.filter{
    it == 42
}

mutableList2.forEach{
	println(it)
}

Функция map()

И еще одна полезная функция для коллекций – map(). Используется, если нам нужно взаимодействовать со всеми элементами. Конечно, все это можно сделать в цикле for, но так изящнее и соответствует стилю функционального программирования. Применив функцию map(), можно обойти все элементы списка и записать их преобразования в новый список. Для демонстрации подойдет любая простая операция. Умножим на 2 все элементы из списка mutableList2 – там сейчас содержится два числа 42. Сохраним результат преобразования в новый список mutableList3 и выведем его в консоль:

val mutableList3 = mutableList2.map{
    it * 2
}

println(mutableList3)

Далее на практике написания приложений мы часто будем сталкиваться с функциями-расширениями и даже научимся создавать свои. Сейчас это все, что я хотел сказать по стандартным функциям для коллекций.

Для тех, кто собрался стать Android-разработчиком

Пошаговаясхема
Пошаговая
схема

Описание процесса обучения от основ Kotlin до Android-разработчика

Бесплатныеуроки
Бесплатные
уроки

Авторский бесплатный курс по основам языка программирования Kotlin

Обучающийбот
Обучающий
бот

Тренажер и самоучитель по Котлин – бесплатные тесты и практика

Поделиться уроком

Ответить

Ваш адрес email не будет опубликован. Обязательные поля помечены *