Статьи

Котлин с нуля: диапазоны и коллекции

Kotlin — это современный язык программирования, который компилируется в байт-код Java. Он бесплатный и с открытым исходным кодом , и обещает сделать кодирование для Android еще более увлекательным.

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

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

Теперь мы рассмотрим различные способы создания диапазонов в Kotlin.

1
val oneToFive = 1..5

В приведенном выше коде мы создали закрытый диапазон. Эта переменная oneToFive будет содержать следующие значения: 1, 2, 3, 4, 5. Мы можем зациклить ее, используя конструкцию цикла for .

1
2
3
for (n in oneToFive) {
    print(n)
}

Код выше можно сократить до:

1
2
3
for (n in 1..5) {
    print(n)
}

Мы также можем создать диапазон символов:

1
val aToZ = «a»..»z»

Переменная aToZ будет иметь все буквы английского алфавита.

Оператор .. можно заменить на rangeTo() расширения rangeTo() для создания диапазона. Например, мы также можем сделать это 1.rangeTo(5) и он все равно будет иметь те же результаты, что и оператор .. как обсуждалось ранее.

1
val oneToFive: IntRange = 1.rangeTo(5)

Это еще одна функция расширения, которая создает диапазон, начиная с данного числа и заканчивая другим.

1
val fiveToOne = 5.downTo(1)

Мы можем изменить диапазон, используя функцию step() . Это изменит дельту между каждым элементом в диапазоне.

1
val oneToTenStep = 1..10 step 2 // 1, 3, 5, 7, 9

Код выше будет содержать нечетные числа от 1 до 10.

Оператор in используется для определения наличия значения в данном диапазоне.

1
2
3
if (5 in 1..10) {
    print(«Yes 5 is in the range») // prints «Yes 5 is in the range»
}

В приведенном выше коде мы проверили, находится ли 5 ​​в диапазоне 1..10, используя оператор in . Мы также можем сделать обратное, используя !n чтобы проверить, не находится ли 5 ​​в диапазоне.

Коллекции используются для хранения групп связанных объектов в памяти. В коллекции мы можем извлекать, хранить или упорядочивать объекты. Kotlin предоставляет API своих коллекций в качестве стандартной библиотеки, построенной на основе API Java Collections. (Мы обсудим интерфейсы в Kotlin в следующем посте.)

Следует отметить, что эти интерфейсы связаны с их реализацией во время компиляции. Вы не можете видеть исходный код реализации в Kotlin, потому что коллекции фактически реализуются стандартными коллекциями Java, такими как ArrayList , Maps , HashMap , Sets , HashSet , List и так далее. Чтобы действительно понять API коллекций в Kotlin, вы должны быть знакомы с этими базовыми классами и интерфейсами в Java.

В этом разделе мы узнаем о коллекциях List , Set и Map в Kotlin. (Если вы хотите освежить в массивах в Kotlin, пожалуйста, посетите первый учебник в этой серии .)

Коллекции Kotlin дают нам возможность многого достичь с помощью небольшого кода — в отличие от Java, который, кажется, нуждается в большом количестве кода, чтобы чего-то достичь! У Котлина есть два варианта коллекций: изменяемые и неизменяемые. Изменяемая коллекция предоставляет нам возможность изменять коллекцию путем добавления, удаления или замены элемента. Неизменяемые коллекции не могут быть изменены и не имеют этих вспомогательных методов.

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

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

1
2
3
public interface Iterable<out T> {
    public abstract operator fun iterator(): Iterator<T>
}

Интерфейс Kotlin Collection расширяет интерфейс Iterable . Интерфейс Collection является неизменным. Другими словами, у вас есть доступ только для чтения к коллекциям. Интерфейсы Set и List (подробнее об этом ниже) в Kotlin расширяют этот интерфейс.

Некоторые из функций и свойств, доступных в интерфейсе Collection :

  • size : это свойство возвращает размер коллекции.
  • isEmpty() : возвращает true, если коллекция пуста или false в противном случае.
  • contains(element: E) : возвращает true, если элемент, указанный в аргументе, присутствует в коллекции.
  • containsAll(element: Collection<E>) : возвращает true, если элемент в коллекции, переданный в качестве аргумента, присутствует в коллекции.
1
2
3
4
5
6
7
public interface Collection<out E> : Iterable<E> {
    public val size: Int
    public fun isEmpty(): Boolean
    public operator fun contains(element: @UnsafeVariance E): Boolean
    override fun iterator(): Iterator<E>
    public fun containsAll(elements: Collection<@UnsafeVariance E>): Boolean
}

Этот интерфейс в Kotlin дает нам специализированный изменяемый итератор из родительского интерфейса Iterable .

1
2
3
public interface MutableIterable<out T> : Iterable<T> {
    override fun iterator(): MutableIterator<T>
}

Интерфейс MutableCollection в Kotlin — это специализированный интерфейс, который позволяет изменять коллекции. Другими словами, операции добавления и удаления могут быть выполнены для данной коллекции. Этот интерфейс расширяет как интерфейс Collection интерфейс MutableIterable уже рассмотренный выше. Интерфейсы MutableSet и MutableList (мы вскоре к ним вернемся) в Kotlin расширяют этот интерфейс. Функции, доступные в этом интерфейсе, кроме функций, доступных в его родителях:

  • add(element: E) : добавляет элемент, переданный в качестве аргумента, в коллекцию и возвращает true в случае успеха или false, если коллекция не поддерживает дубликаты и элемент уже присутствует.
  • remove(element: E) : удаляет элемент, переданный в качестве аргумента из коллекции. Возвращает true, если успешно, или false, если его не было в коллекции.
  • addAll(elements: Collection<E>) : добавляет все элементы в коллекции, переданные в качестве аргументов в коллекцию. Возвращает true, если успешно, или false, если ничего не было добавлено.
  • removeAll(elements: Collection<E>) : удаляет все элементы, присутствующие в коллекции, переданные в качестве аргументов. Возвращает true, если успешно, или false, если ничего не было удалено.
  • retainAll(elements: Collection<E>) : сохраняет только элементы, присутствующие в коллекциях, переданных в качестве аргументов. Возвращает true, если успешно, или false, если ничего не было сохранено.
  • clear() : удаляет все элементы из этой коллекции.
1
2
3
4
5
6
7
8
9
public interface MutableCollection<E> : Collection<E>, MutableIterable<E> {
    override fun iterator(): MutableIterator<E>
    public fun add(element: E): Boolean
    public fun remove(element: E): Boolean
    public fun addAll(elements: Collection<E>): Boolean
    public fun removeAll(elements: Collection<E>): Boolean
    public fun retainAll(elements: Collection<E>): Boolean
    public fun clear(): Unit
}

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

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

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

1
2
3
4
5
var numbers: List<Int> = listOf(1, 2, 3, 4, 5)
var names: List<String> = listOf(«Chike», «Nnamdi», «Mgbemena»)
for (name in names) {
    println(name)
}

Выполнение кода выше выведет:

1
2
3
Chike
Nnamdi
Mgbemena

Более того, мы можем передавать значения различных типов в listOf() качестве аргументов, и результат все равно будет работать — это будет список смешанного типа.

1
var listMixedTypes = listOf(«Chike», 1, 2.445, ‘s’) // will still compile

Эта функция просто создает пустой неизменяемый список и возвращает тип интерфейса Kotlin List .

1
val emptyList: List<String> = emptyList<String>()

Эта функция создает новый неизменный список, содержащий только те элементы, которые не равны NULL. Обратите внимание, что эта функция также возвращает тип интерфейса Kotlin List .

1
val nonNullsList: List<String> = listOfNotNull(2, 45, 2, null, 5, null)

Интерфейс List из стандартной библиотеки Kotlin расширяет только интерфейс Collection . Другими словами, его единственным родителем является интерфейс Collection . Он переопределяет все функции в родительском интерфейсе для удовлетворения его особых потребностей, а также определяет свои собственные функции, такие как:

  • get(index: Int) : оператор функции, который возвращает элемент по указанному индексу.
  • indexOf(element: E) : возвращает индекс первого вхождения элемента, переданного в качестве аргумента в списке, или -1, если ничего не найдено.
  • lastIndexOf(element: E) : возвращает индекс последнего вхождения элемента, переданного в качестве аргумента в списке, или -1, если ничего не найдено.
  • listIterator() : возвращает итератор списка для элементов в списке.
  • subList(fromIndex: Int, toIndex: Int) : возвращает список, содержащий часть списка между указанными начальным и конечным индексами.
1
2
3
4
println(names.size) // 3
println(names.get(0)) // «Chike»
println(names.indexOf(«Mgbemena»)) // 2
println(names.contains(«Nnamdi»)) // ‘true’

Это создает изменяемый список и возвращает тип Java ArrayList .

1
val stringList: ArrayList<String> = arrayListOf<String>(«Hello», «You», «There»)

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

1
2
var mutableNames1 = names.toMutableList()
mutableNames1.add(«Ruth») // now mutable and added «Ruth» to list

Чтобы создать изменяемый список определенного типа с нуля, например, String , мы используем mutableListOf<String>() , в то время как для смешанных типов мы можем просто использовать вместо этого функцию mutableListOf() .

1
2
3
4
5
6
7
8
// a mutable list of a certain type eg String
val mutableListNames: MutableList<String> = mutableListOf<String>(«Josh», «Kene», «Sanya»)
mutableListNames.add(«Mary»)
mutableListNames.removeAt(1)
mutableListNames[0] = «Oluchi» // replaces the element in index 0 with «Oluchi»
 
// a mutable list of mixed types
val mutableListMixed = mutableListOf(«BMW», «Toyota», 1, 6.76, ‘v’)

Любая из этих функций возвращает MutableList интерфейса MutableList Kotlin. Этот интерфейс расширяет интерфейсы MutableCollection и List рассмотренные ранее в этом разделе. Интерфейс MutableList добавляет методы для поиска или замены элемента на основе его позиции:

  • set(index: Int, element: E) : заменяет элемент в списке другим элементом. Это возвращает элемент ранее в указанной позиции.
  • add(index: Int, element: E) : вставляет элемент по указанному индексу.
  • removeAt(index: Int) : избавляется от элемента по определенному индексу.
1
2
3
4
5
6
7
8
9
val mutableListFood: MutableList<String> = mutableListOf<String>(«Rice & stew», «Jollof rice», «Eba & Egusi», «Fried rice»)
mutableListFood.remove(«Fried rice»)
mutableListFood.removeAt(0)
mutableListFood.set(0, «Beans»)
mutableListFood.add(1, «Bread & tea»)
 
for (foodName in mutableListFood) {
    println(foodName)
}

Запустив приведенный выше код, мы получим следующий результат:

1
2
3
Beans
Bread & tea
Eba & Egusi

Обратите внимание, что все эти функции создают Java ArrayList за кулисами.

Набор — это неупорядоченная коллекция уникальных элементов. Другими словами, он не может иметь дубликатов! Давайте посмотрим на некоторые из различных способов создания набора в Kotlin. Каждый из них создает различную структуру данных, каждая из которых оптимизирована для определенного вида задач.

Чтобы создать неизменяемый (только для чтения) набор в Kotlin, мы можем использовать функцию setOf() , которая возвращает тип интерфейса Kotlin Set .

1
2
3
4
// creates a immutable set of mixed types
val mixedTypesSet = setOf(2, 4.454, «how», «far», ‘c’) // will compile
 
var intSet: Set<Int> = setOf(1, 3, 4) // only integers types allowed

Обратите внимание, что интерфейс Kotlin Set расширяет только интерфейс Kotlin Collection и переопределяет все свойства, доступные в его родительском элементе.

Использование функции hashSetOf() создает коллекцию Java HashSet которая хранит элементы в хэш-таблице. Поскольку эта функция возвращает тип Java HashSet , мы можем добавлять, удалять или очищать элементы в наборе. Другими словами, это изменчиво.

1
2
3
val intsHashSet: java.util.HashSet<Int> = hashSetOf(1, 2, 6, 3)
intsHashSet.add(5)
intsHashSet.remove(1)

Использование функции sortedSetOf() создает TreeSet коллекцию Java TreeSet , которая упорядочивает элементы на основе их естественного упорядочения или компаратора. Этот набор также изменчив.

1
2
3
4
val intsSortedSet: java.util.TreeSet<Int> = sortedSetOf(4, 1, 7, 2)
intsSortedSet.add(6)
intsSortedSet.remove(1)
intsSortedSet.clear()

Эта функция возвращает тип Java LinkedHashSet . Этот изменяемый набор поддерживает связанный список записей в наборе в том порядке, в котором они были вставлены.

1
2
3
4
val intsLinkedHashSet: java.util.LinkedHashSet<Int> = linkedSetOf(5, 2, 7, 2, 5) // 5, 2, 7
intsLinkedHashSet.add(4)
intsLinkedHashSet.remove(2)
intsLinkedHashSet.clear()

Мы можем использовать mutableSetOf() для создания изменяемого набора. Эта функция возвращает MutableSet интерфейса Kotlin MutableSet . За кулисами эта функция просто создает Java LinkedHashSet .

1
2
3
4
// creates a mutable set of int types only
val intsMutableSet: MutableSet<Int> = mutableSetOf(3, 5, 6, 2, 0)
intsMutableSet.add(8)
intsMutableSet.remove(3)

Интерфейс MutableSet расширяет интерфейсы MutableCollection и Set .

Карты ассоциируют ключи со значениями. Ключи должны быть уникальными, но связанные значения не должны быть. Таким образом, каждый ключ можно использовать для уникальной идентификации связанного значения, поскольку карта гарантирует, что в коллекции не будет дублированных ключей. За кулисами Kotlin использует коллекцию Java Map для реализации своего типа коллекции карт.

Чтобы создать неизменяемый или доступный только для чтения набор Map в Kotlin, мы используем mapOf() . Мы создаем карту с этой функцией, предоставляя ей список пар — первое значение — это ключ, а второе — это значение. Вызов этой функции возвращает тип интерфейса Kotlin Map .

1
2
3
4
5
val callingCodesMap: Map<Int, String> = mapOf(234 to «Nigeria», 1 to «USA», 233 to «Ghana»)
for ((key, value) in callingCodesMap) {
    println(«$key is the calling code for $value»)
}
print(callingCodesMap[234]) // Nigeria

Выполнение приведенного выше кода даст результат:

1
2
3
234 is the calling code for Nigeria
1 is the calling code for USA
233 is the calling code for Ghana

В отличие от интерфейсов List и Set в Kotlin, которые расширяют интерфейс Collection интерфейс Map вообще не расширяется. Некоторые из свойств и функций, доступных в этом интерфейсе:

  • size : это свойство возвращает размер коллекции карт.
  • isEmpty() : возвращает true, если карта пуста или false в противном случае.
  • containsKey(key: K) : возвращает true, если карта содержит ключ в аргументе.
  • containsValue(value: V) : возвращает true, если карта отображает один или несколько ключей на значение, переданное в качестве аргумента.
  • get(key: K) : возвращает значение, соответствующее данному ключу, или ‘null’, если ничего не найдено.
  • keys : это свойство возвращает неизменный Set всех ключей на карте.
  • values : возвращает неизменную Collection всех значений на карте.

Функция mutableMapOf() создает для нас изменяемую карту, чтобы мы могли добавлять и удалять элементы в карте. Это возвращает MutableMap интерфейса Kotlin MutableMap .

1
2
3
4
5
val currenciesMutableMap: MutableMap<String, String> = mutableMapOf(«Naira» to «Nigeria», «Dollars» to «USA», «Pounds» to «UK»)
println(«Countries are ${currenciesMutableMap.values}») // Countries are [Nigeria, USA, UK]
println(«Currencies are ${currenciesMutableMap.keys}») // Currencies are [Naira, Dollars, Pounds]
currenciesMutableMap.put(«Cedi», «Ghana»)
currenciesMutableMap.remove(«Dollars»)

Интерфейс MutableMap не расширяет интерфейс MutableCollection ; это единственный родительский интерфейс Map . Он переопределяет keys , entries и values свойств из родительского интерфейса, чтобы переопределить их. Вот некоторые из дополнительных функций, доступных в интерфейсе MutableMap :

  • put(key: K, value: V) : вставляет пару ключ-значение в карту. Это вернет предыдущее значение, связанное с ключом, или ноль, если ключ ранее не использовался.
  • remove(key: K) : удаляет ключ и его связанное значение с карты.
  • putAll (from: Map<out K, V>) : обновляет карту со всеми данными с данной карты. Будут добавлены новые ключи, а существующие ключи будут обновлены с новыми значениями.
  • clear() : удаляет все элементы с карты.

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

1
2
print(currenciesMutableMap.get(«Nigeria»)) // will print Naira
print(currenciesMutableMap[«Nigeria»]) // will print Naira

Использование этой функции возвращает тип Java HashMap который является изменяемым. Класс HashMap использует хеш-таблицу для реализации интерфейса Java Map .

1
2
3
4
val personsHashMap: java.util.HashMap<Int, String> = hashMapOf(1 to «Chike», 2 to «John», 3 to «Emeka»)
personsHashMap.put(4, «Chuka»)
personsHashMap.remove(2)
print(personsHashMap[1]) // will print Chike

Эта функция возвращает тип Java LinkedHashMap который является изменяемым. Класс LinkedHashMap расширяет Java HashMap и поддерживает связанный список записей на карте в том порядке, в котором они были вставлены.

1
2
3
4
5
val postalCodesHashMap: java.util.LinkedHashMap<String, String> =
linkedMapOf(«NG» to «Nigeria»,»AU» to «Australia»,»CA» to «Canada»)
postalCodesHashMap.put(«NA», «Namibia»)
postalCodesHashMap.remove(«AU»)
postalCodesHashMap.get(«CA») // Canada

Эта функция возвращает тип Java SortedMap который является изменяемым. Класс Java SortedMap видит, что записи на карте поддерживаются в порядке возрастания ключа.

1
2
3
val personsSortedMap: java.util.SortedMap<Int, String> = sortedMapOf(2 to «Chike», 1 to «John», 3 to «Emeka»)
personsSortedMap.put(7, «Adam»)
personsSortedMap.remove(3)

Помните, что реализация этих коллекционных интерфейсов в Kotlin происходит во время компиляции.

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

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

1
2
3
4
5
6
7
8
val stringList: List<String> = listOf(«in», «the», «club»)
print(stringList.last()) // will print «club»
 
// given a predicate
print(stringList.last{ it.length == 3}) // will print «the»
 
val intSet: Set<Int> = setOf(3, 5, 6, 6, 6, 3)
print(intSet.last()) // will print 6

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

1
2
print(stringList.first()) // will print «in»
print(intSet.first()) // will print 3

Вызов этой операторной функции в коллекции, такой как список или набор, возвращает наибольший элемент, или ноль, если наибольший элемент не найден.

1
2
3
val intList: List<Int> = listOf(1, 3, 4)
print(intList.max()) // will print 4
print(intSet.max()) // will print 6

Вызов этой операторной функции возвращает новый список или набор, содержащий все элементы, кроме первых n элементов.

1
print(stringList.drop(2)) // will print «club»

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

1
print(intList.plus(6)) // will print [1, 3, 4, 6]

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

1
print(intList.minus(3)) // will print [1, 4]

Вызов этой операторной функции вернет среднее количество элементов в коллекции.

1
print(intList.average()) // will print 2.6666666666666665

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

В этом уроке вы узнали об API диапазона и коллекций на языке программирования Kotlin. В следующем уроке из серии Kotlin From Scratch вы познакомитесь с функциями в Kotlin. До скорого!

Чтобы узнать больше о языке Kotlin, я рекомендую посетить документацию Kotlin . Или посмотрите некоторые другие наши посты по разработке приложений для Android здесь на Envato Tuts!

  • Android SDK
    Введение в компоненты архитектуры Android
    Жестяная мегали
  • Машинное обучение
    Создание интеллектуальных чат-ботов на Android с IBM Watson
    Ашраф Хатхибелагал
  • Android SDK
    Java против Kotlin: стоит ли использовать Kotlin для разработки под Android?
    Джессика Торнсби
  • Android SDK
    Совет: пишите более чистый код с помощью Kotlin SAM Conversions
    Ашраф Хатхибелагал
  • Android SDK
    Android O: проверка номера телефона с помощью SMS-токенов