- Массивы и Cписки в Kotlin — Полное Руководство
- Массивы в Kotlin
- Что такое массив?
- Когда лучше использовать массивы?
- Создание массивов в Kotlin
- Массивы примитивных типов IntArray, FloatArray, DoubleArray
- Циклы for, while, do-while, forEach, repeat()
- Интервалы
- Списки и множества
- Map
- Массивы
- Вложенные циклы for
- forEach
- repeat()
- while
- do-while
- Conditions and loops
- When expression
- For loops
- While loops
- Break and continue in loops
Массивы и Cписки в Kotlin — Полное Руководство
Коллекции представляют собой гибкие «контейнеры», которые позволяют хранить вместе любое количество значений. Двумя самыми популярными типами коллекций являются массивы и списки.
Массивы в Kotlin
Массив в Kotlin соответствует базовому типу массива в Java. Массивы являются типизированными, как обычные переменные и константы, и они могут хранить много значений.
Перед созданием первого массива давайте подробнее рассмотрим, что из себя представляет массив и зачем он используется.
Что такое массив?
Массив является упорядоченной коллекцией значений одного типа. У элементов в массиве нулевая индексация. Это означает, что индекс первого элемента равен 0, индекс второго элемента равен 1 и так далее. Зная это, вы можете определить, что индекс последнего элемента будет на 1 меньше общего числа значений в массиве.
В данном массиве пять элементов с индексами от 0 до 4.
Все его значения имеют тип String , поэтому в массив, содержащий только строки, нельзя добавить значения другого типы кроме строк . Обратите внимание, что одно и то же значение может встречаться несколько раз.
Когда лучше использовать массивы?
Массивы пригодятся при необходимости хранить элементы в определенном порядке. Элементы можно будет отсортировать или сделать их выборку по индексу без итерации по всему массиву.
К примеру, при хранении данных о спортивных рекордах, порядок очень важен. Наивысший балл должен быть первым в списке (его индекс 0), далее идет второй лучший балл и так далее.
Создание массивов в Kotlin
Самым простым способом создания нового массива является использование функции arrayOf() из стандартной библиотеки Kotlin. Таким образом можно быстро указать какие значения должен хранить массив.
Поскольку данный массив содержит только целые числа, Kotlin воспринимает evenNumbers как массив значений типа Int . Этот тип записывается как Array . Тип внутри угловых скобок определяет тип значений, которые может хранить массив. Именно тип компилятор будет использовать при добавлении элементов в массив.
Если вы попытаетесь добавить строку в массиве в котором только числа, то компилятор вернет ошибку, и код не скомпилируется. Такой синтаксис для типа массива является примером аргумента типа или дженерики, о котором вы узнаете подробнее в следующем уроке.
Также, возможно создать массив, у которого все значения будут значениями по умолчанию:
Подробнее о таком синтаксисе < 5 >мы поговорим в уроке о лямбдах.
Как и с любым другим типом, лучше объявлять массивы, которые не будут меняться, то есть как константы. Рассмотрим следующий массив:
Константа vowels является массивом из строк, значения которых изменить нельзя. Это правильно, потому что список гласных букв уже давно не менялся.
Массивы примитивных типов IntArray, FloatArray, DoubleArray
При использовании функции arrayOf() и создании массивов с типами вроде Array итоговый массив представляет собой список из объектов. В частности, если вы работаете в JVM, целочисленный тип будет упакован как класс Integer , а не как примитивный тип int . Использование примитивных типов по сравнению с их упакованными аналогами в виде классов потребляет меньше памяти и приводит к повышению производительности. К сожалению, вы не можете использовать примитивы со списками (которые рассматриваются в следующем разделе), поэтому решение, стоит ли это делать, нужно принимать для каждого отдельного случая.
Стандартная библиотека Kotlin содержит и другие функции, не только arrayOf() , которые позволяют создавать массивы, соответствующие массивам примитивных типов. К примеру, вы можете создать массив нечетных чисел следующим образом:
Циклы for, while, do-while, forEach, repeat()
Цикл for в Kotlin имеет другой синтаксис. В Java нам нужно инициализировать переменную перед началом цикла, затем указать условие и затем изменять переменную.
Это классический вариант, который применяется и в других языках. Но он довольно неудобный и не понятен для новичков. Например, часто путаются в использовании и .
В Kotlin упростили цикл. Для работы с циклом нужен итератор — массив, Map, интервал чисел и т.д.
Интервалы
Стандартный вариант, когда нужно пробежаться по заданному числу элементов, описывается следующим образом. Если для цикла используется одна команда, можно обойтись без фигурных скобок, но проще всегда использовать блок.
for(i in 1..5) println(i) // Лучше со скобками for(i in 1..5)
Оператор in и его брат !in проверяют вхождение или отсутствие вхождения в диапазон.
В выражении 1..5 мы указываем диапазон от 1 до 5 (пять входит в диапазон). Переменная последовательно примет значение
Диапазон можно заменить переменной.
val range = 1..5 for(i in range)
Списки и множества
fun main() < val list = listOf("C", "A", "T") for (letter in list) < print(letter) >// Можно явно указать тип for (str: String in setOf("C", "A", "T")) < print(str) >>
Map
val capitals = mapOf( "USA" to "Washington DC", "England" to "London", "France" to "Paris" ) for ((country, capital) in capitals)
Массивы
val cats = arrayListOf() cats.add("Мурзик") cats.add("Васька") cats.add("Барсик") for(cat in cats) < println("Кот $cat") >// Результат Кот Мурзик Кот Васька Кот Барсик
Этот же пример можно переписать, используя индекс:
Проверим, является ли символ не цифрой.
fun isNotDigit(c: Char) = c !in '0'..'9' println(isNotDigit('3')) // false, это всё-таки цифра
С помощью index, element и withIndex() можно получить индекс и его значение в массиве.
val cats = arrayListOf("Barsik", "Murzik", "Vaska") for( (index, element) in cats.withIndex())
0: Мурзик 1: Васька 2: Барсик
А как быть с вариантами, когда нужно счётчик не увеличивать на единицу, а уменьшать на 2? Используем ключевые слова downTo и step.
for(i in 10 downTo 1 step 2) < print("$i ") >// выводит: 10 8 6 4 2
Без указания шага значения будут уменьшаться на единицу.
for(i in 10 downTo 1) < print("$i ") >// 10 9 8 7 6 5 4 3 2 1 for (letter in 'Z' downTo 'A') print(letter) // ZYXWVUTSRQPONMLKJIHGFEDCBA
Очень часто в циклах встречается выражение size-1, чтобы не выйти за пределы массива. Можно заменить на until.
for(i in 0..cats.size - 1) < println(cats[i]) >for(i in 0 until cats.size)
Хотим выйти из цикла при достижении какого-то значения.
for (i in 1..5) < println(i) // прекращаем перебор, когда достигнем значения 3 if (i == 3) break >// Получим 1 2 3
Ключевое слово continue позволяет продолжить перебор, при этом можно что-то сделать во время перебора, например, мяукнуть. Число 5 при этом не выводится.
println("For loop 1 to 10 continue if number is 5") for (i in 1..10) < if (i == 5) < println("Meow") continue >println(i) > // Результат For loop 1 to 10 continue if number is 5 1 2 3 4 Meow 6 7 8 9 10
Вложенные циклы for
Часто используют два цикла for. Простейший вариант.
// первый цикл for(x in 1..3) < // второй вложенный цикл for(y in 1..3)< print("$x $y \n") >> // результат 1 1 1 2 1 3 2 1 2 2 2 3 3 1 3 2 3 3
Если мы хотим досрочно прервать на каком-то этапе цикл, то можем добавить аннотацию outerLoop@ к первому циклу и внутри второго цикла вызвать [email protected]. Как только значения станут равными (2 2), цикл завершится досрочно.
// первый цикл outerLoop@ for (x in 1..3) < // второй вложенный цикл for (y in 1..3) < print("$x $y \n") if (x == 2 && y == 2) brea[email protected] > > // результат 1 1 1 2 1 3 2 1 2 2
forEach
Пройтись по всем элементам коллекции.
val daysOfWeek = listOf("Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday") daysOfWeek.forEach
repeat()
Встроенная функция для повторения команд заданное число раз.
while
Цикл while работает стандартным способом — пока условие остаётся истинным, выполняются команды в блоке цикла. Если код после условия цикла состоит из одной строчки, то фигурные скобки можно опустить.
var i = 1 while (i < 9) < println(i) i = i + 1 >// вышли из цикла и проверяем последнее значение переменной println(i) // 9
Обратите внимание, что внутри цикла мы получим числа от 0 до 8, но если проверить, чему равно значение переменной после цикла, то увидим, что оно больше.
Прервать цикл можно через break.
var i = 1 while (i < 9) < if (i == 5)< break >println(i) i += 1 > // результат 1 2 3 4
do-while
Выполняем какую-то работу, пока выполняется какое-то условие — выводим число, которое увеличивается на единицу, пока число меньше 10.
Можно прервать цикл через break. Когда число станет равным 5, цикл прекратится.
Conditions and loops
In Kotlin, if is an expression: it returns a value. Therefore, there is no ternary operator ( condition ? then : else ) because ordinary if works fine in this role.
fun main() < val a = 2 val b = 3 //sampleStart var max = a if (a < b) max = b // With else if (a >b) < max = a >else < max = b >// As expression max = if (a > b) a else b // You can also use `else if` in expressions: val maxLimit = 1 val maxOrLimit = if (maxLimit > a) maxLimit else if (a > b) a else b //sampleEnd println(«max is $max») println(«maxOrLimit is $maxOrLimit») >
Branches of an if expression can be blocks. In this case, the last expression is the value of a block:
If you’re using if as an expression, for example, for returning its value or assigning it to a variable, the else branch is mandatory.
When expression
when defines a conditional expression with multiple branches. It is similar to the switch statement in C-like languages. Its simple form looks like this.
when matches its argument against all branches sequentially until some branch condition is satisfied.
when can be used either as an expression or as a statement. If it is used as an expression, the value of the first matching branch becomes the value of the overall expression. If it is used as a statement, the values of individual branches are ignored. Just like with if , each branch can be a block, and its value is the value of the last expression in the block.
The else branch is evaluated if none of the other branch conditions are satisfied.
If when is used as an expression, the else branch is mandatory, unless the compiler can prove that all possible cases are covered with branch conditions, for example, with enum class entries and sealed class subtypes).
enum class Bit < ZERO, ONE >val numericValue = when (getRandomBit()) < Bit.ZERO ->0 Bit.ONE -> 1 // ‘else’ is not required because all cases are covered >
In when statements, the else branch is mandatory in the following conditions:
- when has a subject of a Boolean , enum , or sealed type, or their nullable counterparts.
- branches of when don’t cover all possible cases for this subject.
enum class Color < RED, GREEN, BLUE >when (getColor()) < Color.RED ->println(«red») Color.GREEN -> println(«green») Color.BLUE -> println(«blue») // ‘else’ is not required because all cases are covered > when (getColor()) < Color.RED ->println(«red») // no branches for GREEN and BLUE else -> println(«not red») // ‘else’ is required >
To define a common behavior for multiple cases, combine their conditions in a single line with a comma:
You can use arbitrary expressions (not only constants) as branch conditions
You can also check a value for being in or !in a range or a collection:
when (x) < in 1..10 ->print(«x is in the range») in validNumbers -> print(«x is valid») !in 10..20 -> print(«x is outside the range») else -> print(«none of the above») >
Another option is checking that a value is or !is of a particular type. Note that, due to smart casts, you can access the methods and properties of the type without any extra checks.
when can also be used as a replacement for an if — else if chain. If no argument is supplied, the branch conditions are simply boolean expressions, and a branch is executed when its condition is true:
You can capture when subject in a variable using following syntax:
fun Request.getBody() = when (val response = executeRequest()) < is Success ->response.body is HttpError -> throw HttpException(response.status) >
The scope of variable introduced in when subject is restricted to the body of this when.
For loops
The for loop iterates through anything that provides an iterator. This is equivalent to the foreach loop in languages like C#. The syntax of for is the following:
The body of for can be a block.
As mentioned before, for iterates through anything that provides an iterator. This means that it:
- has a member or an extension function iterator() that returns Iterator<> :
- has a member or an extension function next()
- has a member or an extension function hasNext() that returns Boolean .
All of these three functions need to be marked as operator .
To iterate over a range of numbers, use a range expression:
A for loop over a range or an array is compiled to an index-based loop that does not create an iterator object.
If you want to iterate through an array or a list with an index, you can do it this way:
Alternatively, you can use the withIndex library function:
While loops
while and do-while loops execute their body continuously while their condition is satisfied. The difference between them is the condition checking time:
- while checks the condition and, if it’s satisfied, executes the body and then returns to the condition check.
- do-while executes the body and then checks the condition. If it’s satisfied, the loop repeats. So, the body of do-while executes at least once regardless of the condition.
Break and continue in loops
Kotlin supports traditional break and continue operators in loops. See Returns and jumps.