Массивы и Cписки в Kotlin — Полное Руководство

Коллекции представляют собой гибкие «контейнеры», которые позволяют хранить вместе любое количество значений. Двумя самыми популярными типами коллекций являются массивы и списки.

Содержание статьи

Массивы в Kotlin

Массив в Kotlin соответствует базовому типу массива в Java. Массивы являются типизированными, как обычные переменные и константы, и они могут хранить много значений.

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

Что такое массив?

Массив является упорядоченной коллекцией значений одного типа. У элементов в массиве нулевая индексация. Это означает, что индекс первого элемента равен 0, индекс второго элемента равен 1 и так далее. Зная это, вы можете определить, что индекс последнего элемента будет на 1 меньше общего числа значений в массиве.

массивы в kotlin

В данном массиве пять элементов с индексами от 0 до 4.

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

Когда лучше использовать массивы?

Массивы пригодятся при необходимости хранить элементы в определенном порядке. Элементы можно будет отсортировать или сделать их выборку по индексу без итерации по всему массиву.

К примеру, при хранении данных о спортивных рекордах, порядок очень важен. Наивысший балл должен быть первым в списке (его индекс 0), далее идет второй лучший балл и так далее.

Создание массивов в Kotlin

Самым простым способом создания нового массива является использование функции arrayOf() из стандартной библиотеки Kotlin. Таким образом можно быстро указать какие значения должен хранить массив.

Поскольку данный массив содержит только целые числа, Kotlin воспринимает evenNumbers как массив значений типа Int. Этот тип записывается как Array<Int>. Тип внутри угловых скобок определяет тип значений, которые может хранить массив. Именно тип компилятор будет использовать при добавлении элементов в массив.

Если вы попытаетесь добавить строку в массиве в котором только числа, то компилятор вернет ошибку, и код не скомпилируется. Такой синтаксис для типа массива является примером аргумента типа или дженерики, о котором вы узнаете подробнее в следующем уроке.

Также, возможно создать массив, у которого все значения будут значениями по умолчанию:

Подробнее о таком синтаксисе { 5 } мы поговорим в уроке о лямбдах.

Как и с любым другим типом, лучше объявлять массивы, которые не будут меняться, то есть как константы. Рассмотрим следующий массив:

Константа vowels является массивом из строк, значения которых изменить нельзя. Это правильно, потому что список гласных букв уже давно не менялся.

Массивы примитивных типов IntArray, FloatArray, DoubleArray

При использовании функции arrayOf() и создании массивов с типами вроде Array<Int> итоговый массив представляет собой список из объектов. В частности, если вы работаете в JVM, целочисленный тип будет упакован как класс Integer, а не как примитивный тип int. Использование примитивных типов по сравнению с их упакованными аналогами в виде классов потребляет меньше памяти и приводит к повышению производительности. К сожалению, вы не можете использовать примитивы со списками (которые рассматриваются в следующем разделе), поэтому решение, стоит ли это делать, нужно принимать для каждого отдельного случая.

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

При запуске Kotlin в JVM, массив oddNumbers компилируется в Java массив типа int[].

К другим функциям стандартной библиотеки относятся:

  • floatArrayOf()
  • doubleArrayOf()
  • booleanArrayOf()

Данные функции создают массивы типов IntArray, FloatArray, DoubleArray и так далее. Вы также можете передать число в конструктор для данных типов. К примеру, для создания массива нулей.

Выполнить конвертацию между упакованными и примитивными массивами можно с помощью функции toIntArray().

Теперь типом константы otherOddNumbers является IntArray, а не Array<Int>.

Массив с аргументами для функции main()

С функции main() начинается каждая программа на Kotlin. В версии Kotlin 1.3 у функции main появился необязательный параметр args типа Array<String>:

При запуске программы из командной строки вы можете отправить аргументы для функции main, как и в обычной программе которая запускается из командной строки.

Поскольку здесь мы используем IntelliJ IDEA, вы можете отправлять аргументы в функцию main, используя конфигурацию проекта, доступ к которой осуществляется через меню Edit Configurations… на панели инструментов IntelliJ:

массивы kotlin

После этого появится окно Run/Debug Configurations. Убедитесь, что ваша конфигурация выбрана на панели слева, затем добавьте аргументы в поле Program arguments с правой части и нажмите ОК:

массивы kotlin

Итерация по массиву в Kotlin

Чтобы увидеть аргументы, переданные в функцию main, вы можете использовать цикл for, о котором мы рассказывали в прошлых уроках.

Однако для итерации по массиву вместо использования подсчета и интервала в цикле for вы используете переменную вроде arg которая будет содержать значение из массива args в зависимости от текущей итерации по массиву:

Результат:

Значение arg обновляется для каждой итерации по массиву. Для каждого элемента массива args по одной итерации. Использование println(arg) в теле цикла выводит каждый из аргументов, переданных функции main.

Альтернативная форма итерации использует метод forEach от массива (напоминает JavaScript):

Подробнее о синтаксисе так называемого trailing lambda, используемом в вызове forEach, мы поговорим в будущих уроках про Lambda.

Списки в Kotlin

Концептуально списки очень похожи на массивы. Как и в Java, тип List в Kotlin является интерфейсом, у которого есть конкретные реализации типов вроде ArrayList, LinkedList и других. В плане производительности массивы обычно эффективнее списков, однако у списков есть возможность динамически менять свой размер.

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

Создание списков в Kotlin

В стандартной библиотеке Kotlin есть функция для создания списка:

Типом константы innerPlanets является List<String>, где String является еще одним примером аргумента типа. Таким образом innerPlanets можно передать в любую функцию, которой требуется список в качестве аргумента.

Под капотом innerPlanets типом для хранения данных, является ArrayList . Если по какой-то причине вам нужно, чтобы у innerPlanets был тип ArrayList, можно использовать другую функцию из стандартной библиотеки.

После их создания — списки innerPlanets и innerPlanetsArrayList изменить нельзя. Для этого требуется создать изменяемый список.

Пустой список можно создать без передачи аргументов в list(). Поскольку сейчас компилятор не может определить тип, вам нужно использовать объявление типа:

Вы также можете поместить аргумент типа в функцию:

Так как список, возвращаемый из listOf(), является неизменяемым, вы не сможете что-то сделать с данным пустым списком. Пустые списки полезны как стартовая точка для изменяемых списков.

Ни один из списков innerPlanets, innerPlanetsArrayList или subscribers не может быть изменен после создания. Для этого нужно создать изменяемый список.

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

В стандартной библиотеке Kotlin есть функция и для изменяемых списков:

Мы создали в константе outerPlanets изменяемый список, просто на тот случай, если в нашей Солнечной системе внезапно будет открыта новая планета. Вы можете создать пустой изменяемый список, не передавая никаких аргументов в функцию:

Позже будет показано, как добавить или удалить элементы из изменяемого списка.

Получение доступа к элементам списка в Kotlin

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

Использование свойств и методов для списков

Представьте, что вы создаете карточную игру. Вам нужно сохранить имена игроков в списке. Список должен меняться по мере того, как игроки заходят или выходят из игры, поэтому требуется объявить изменяемый список:

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

Перед началом игры необходимо убедиться, что игроков достаточно. Можно использовать метод isEmpty(), чтобы проверить если список не является пустым:

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

На заметку: В будущих уроках вы узнаете больше о свойствах и методах. Пока просто думайте о свойствах как о переменных, которые встроены в значения. Чтобы получить доступ к свойству, поставьте точку после имени константы или переменной, которая хранит значение, и за ней следует имя свойства, к которому вы хотите получить доступ. Аналогично, думайте о методах как о функциях которые встроены в значения.

Время начать игру! Вы решили, что порядок в игре определяется порядком имен в списке. Как бы вы узнали имя первого игрока из списка?

Списки предоставляют метод first() для получения первого объекта из списка:

Вывод значения из currentPlayer поднимает интересный вопрос:

Каким был бы вывод, если бы список players оказался пустым?

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

Аналогично, у списков есть метод last(), который возвращает последнее значение списка или выдает ошибку если список пуст:

Другим способом получить значения из списка является вызов метода minOrNull(). Данный метод возвращает элемент списка с наименьшим значением — не тот, у которого самый маленький индекс!

Если массив содержит строки, тогда будет использоваться алфавитный порядок, в данном случае метод minOrNull() выведет значение "Alice":

Вместо возникновения ошибки при невозможности определить минимальное значение, метод minOrNull() вернет тип null, поэтому вам нужно проверить, является ли возвращаемое значение null.

Конечно, методы first() и minOrNull() не будут всегда возвращать одинаковое значение. К примеру:

Как вы могли догадаться, у списков также есть метод maxOrNull().

На заметку: Свойство size а также методы first(), last(), minOrNull() и maxOrNull() не являются эксклюзивными только для массивов или списков. У каждого типа коллекции есть такие свойства и методы в дополнение к множеству других. Вы узнаете больше об этом в уроке об интерфейсах.

Рассмотренные до сих пор методы полезны, если вы хотите получить первый, последний, минимальный или максимальный элемент из списка или массива. Но что, если нужный вам элемент не может быть получен одним из этих методов?

Индекс элемента из списка или массива

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

Поскольку массивы и списки имеют нулевую индексацию, вы используете индекс 0 для получения значения первого объекта из списка.

Синтаксис индексации эквивалентен вызову метода get() для массива или списка и передачи индекса в качестве аргумента.

Вы можете использовать индексы 1, 2, 3 для получения значений следующих элементов массива или списка, но если вы попытаетесь получить значение индекса который находится за пределами размера массива или списка, вы получите ошибку и программа прекратит свою работу:

Такая ошибка возникла, потому что список players содержит только четыре строки. Индекс 4 запрашивает значение пятого элемента из списка, но в списке нет пятого элемента вообще.

Срез массива или списка в Kotlin

Можно использовать метод slice() с интервалами для получения среза элементов из массива или списка.

К примеру, вот код если вам нужно получить срез имен следующих двух игроков (игнорируя первого):

Используемым интервалом является 1..2. Он указывает на второй и третий элемент массива. Здесь можно использовать индекс, если начальное значение меньше или равно конечному значению, и оба находятся в границах массива. Если начальное значение больше конечного значения, содержимое среза будет пустым.

Объект, возвращаемый методом slice(), представляет собой независимый от оригинала массив или список, поэтому внесение изменений в срез не влияет на исходный массив или список.

Проверка существования элемента в списке

Вы можете выполнить данную проверку с помощью оператора in который возвращает true, если элемент найден в списке, и false если такого значения в списке нет.

Можно использовать эту стратегию для создания функции, которая проверяет, участвует ли определенный игрок в игре или нет:

Оператор ! используется, чтобы инвертировать результат из проверки оператором in, т.е. из true превращает в false и обратно из false в true.

Функция спрашивает «Игрок исключен (isEliminated)?» затем проверяется если такой игрок находится в списке, если он есть в списке игроков тогда функция возвращает false так как он не исключен из игры.

Оператор in является эквивалентом методу contains(). Вы можете проверить наличие элемента в определенном срезе, используя вместе методы slice() и contains():

Теперь, когда вы можете получить данные из массивов и списков, пришло время взглянуть на изменяемые списки и то, как изменить их значения.

Манипуляция со списками в Kotlin

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

Добавление элемента в список

Для участия в игре — новые пользователи должны зарегистрироваться и добавить свои имена в список. Пользователь Eli является первым игроком, который присоединиться к уже существующим участникам.

Вы можете добавить Eli в конец списка через использование метода add():

При попытке добавить что-либо, кроме строки, компилятор выдаст ошибку. Помните, что списки могут хранить только значения одного типа. Кроме того, метод add() работает только с изменяемыми списками.

Следующим игроком, который присоединится к игре, станет Gina. Вы можете добавить её в игру другим способом, используя оператор +=:

Правая часть выражения состоит из одного элемента — строки "Gina". Через использование оператора += вы добавляете элемент в конец списка players. Теперь список выглядит следующим образом:

Последнего игрока мы добавили с помощью оператора += также можно добавить много других участников после Gina.

Хотя у массивов фиксированный размер, на самом деле можно использовать оператор += с массивом, объявленным как переменная.

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

Вставка элемента в список по индексу

Правило нашей карточной игры состоит в том, что имена игроков должны быть в алфавитном порядке. В списке нет игрока, имя которого начинается с буквы F. К счастью, Frank только что прибыл. Его нужно добавить между Eli и Gina. Для этого можно воспользоваться опять методом add(), но при этом указать индекс в качестве первого аргумента:

Первый аргумент определяет, куда добавляется элемент. Помните, что у списка нулевая индексация, поэтому индекс 5 — это индекс игрока Gina, заставляющий ее переместиться дальше, после того как её место занимает Frank.

Удаление элемента из списка в Kotlin

Внезапно выяснилось, что участники Cindy и Gina читеры. Их нужно убрать из игры! Удалить элемент из списка можно с помощью метода remove():

Результат:

Этот метод делает две вещи: удаляет элемент и затем возвращает булево значение, указывающее, что удаление прошло успешно. Таким образом вы будете уверены, что читер и правда удален из игры.

Удаление элемента из списка по индексу

Для удаления Cindy из игры нужно знать индекс, под которым хранится её имя. Взглянув на список игроков, вы увидите, что она третья в списке. Значит, её индекс под номером 2. Для удаления Cindy используем метод removeAt().

Результат:

В отличие от метода remove() который возвращает только true или false, метод removeAt() возвращает значение элемента, который был удален из списка. Теперь можно добавить этот элемент в список читеров!

Как получить индекс элемента из списка?

Но как получить индекс элемента, если вы его не знаете? Для этого существует метод indexOf(), который возвращает первый индекс совпадающего элемента, потому что список может содержать несколько повторов одного и того же значения. Если метод не находит элемент, он возвращает -1.

Задание для проверки №1

Используйте метод indexOf() для определения позиции элемента "Dan" в списке players.

Замена и сортировка элементов из списка

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

Результат:

На протяжении игры некоторые участники проигрывают, и на их место приходят новые. Вы можете использовать индексирование для замены старых игроков новыми:

Результат:

Данный код заменяет игрока Eli на игрока Alice. Затем для сортировки списка в алфавитном порядке вызывается метод sort().

Можно заменять содержимое из элемента путем вызова метода set() для списка.

Вместо:

Согласно IntelliJ IDEА, для таких задач — использование синтаксиса индексации обычно предпочтительнее, чем использование методов get() и set().

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

Итерация по списку в Kotlin

Время позднее, поэтому игроки решили остановиться и продолжить сражение завтра. В это время вы должны сохранить их результаты в отдельном списке. Лучшим вариантом было бы использование карт, про которых мы узнаем позже, а пока что мы используем списки:

Перед тем, как игроки уйдут, вы решаете вывести имена тех, кто на данный момент все еще в игре. Как и в случае с массивами, можно использовать цикл for, о котором мы говорили в одной из предыдущих статей.

Результат:

Данный код просматривает все элементы из списка players, от индекса 0 до players.size - 1, и выводит их значения. В момент первой итерации, переменная player равна первому элементу из списка, во второй итерации она равна второму элементу списка и так далее, пока цикл не выведет все элементы из списка.

Если вам нужен индекс каждого элемента, то можно выполнить итерацию по возвращаемому результату из метода withIndex():

Результат:

Теперь можно использовать изученную технику для создания функции, которая принимает список целых чисел в качестве входных данных и возвращает сумму из этих элементов.

Данную функцию можно использовать для вычисления суммы очков игроков:

Результат:

Задание для проверки №2

Напишите программу при помощи цикла for которая вывела бы на экран имя игрока и количество его очков.

Итоговые задания для проверки

Выполните следующие задания для проверки своих знаний по работе с массивов и списков в Kotlin.

1. Какие из следующих утверждений являются верными?

Для следующих трех случаев array4 был объявлен следующим образом:

Для финальных пяти вариантов array5 был объявлен следующим образом:

2. Напишите функцию, которая удаляет первое число из списка целых чисел. Начало функции должна выглядеть следующим образом:

3. Напишите функцию, которая удаляет все совпадения какого-то целого числа из списка целых чисел. Это сигнатура функции:

4. У массивов и списков есть метод reverse(), который разворачивает в обратном порядке значения из списка или массива. Напишите функцию, которая делает то же самое без использования метода reverse() и возвращает новый массив с теми же элементами, но только в обратном порядке. Это сигнатура функции:

5. Функция ниже возвращает случайное число между from (от — включая его) и to (до — исключая это значение в финальном результате):

Используйте это для написания функции, которая перемешивает элементы массива в случайном порядке. Это сигнатура функции:

6. Напишите функцию, которая подсчитывает минимальное и максимальное значение из массива целых чисел. Подсчитайте эти значения сами. Не используйте методы minOrNull и maxOrNull. Если массив пуст, то возвращаемым результатом должен быть null.

Сигнатура данной функции.

Подсказка: Внутри функции можно использовать константы Int.MIN_VALUE и Int.MAX_VALUE.

Главные особенности списков и массивов в Kotlin

  • Массивы являются упорядоченными наборами значений одного типа;
  • Существуют специальные классы вроде IntArray, которые создаются как массивы примитивных типов в Java;
  • Списки похожи на массивы, но имеют дополнительную возможность динамического изменения размера;
  • Вы можете добавлять, удалять, обновлять и вставлять элементы в изменяемые списки;
  • Используйте индексацию или имеющийся методы для доступа к элементам и их обновления;
  • Остерегайтесь доступа к индексу, который находится за пределами допустимого диапазона списка или массива;
  • Можно перебирать элементы массива или списка, используя цикл for или метод forEach;
  • Можно проверить наличие элементов в массиве или в списке, используя оператор in;

Что дальше?

После изучения массивов и списков в Kotlin мы можем приступить к изучению двух других популярных типов коллекций — карт и множеств.

5 7 голоса
Рейтинг статьи
Подписаться
Уведомить о
guest
2 комментариев
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии
Ruslan
Ruslan
1 год назад

можно узнать логику, почему в ЗАДАНИИ 6 переменные:
var min = Int.MAX_VALUE
var max = Int.MIN_VALUE ?

Я так понимаю решение верное и это не обсуждается, но хотелось бы пару слов о том почему именно так.

brigadir
brigadir
1 год назад

Отличная статья спасибо

2
0
Оставьте комментарий! Напишите, что думаете по поводу статьи.x