Освой Kotlin играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
mapOf()
Создание словаря из списка
Ассоциативные списки (или словари) с уникальными ключами и любыми значениями (дубликаты ключей не допускаются, значения могут быть одинаковыми). Связь между ключами и значениями происходит через специальную форму вызова метода (инфиксный вызов) to. По сути создаётся некоторая таблица, состоящая из ключей и их значений. Каждый ключ в словаре является уникальным, однако значения могут иметь дубликаты.
Неизменяемый Map создаётся через mapOf(). Вы не можете добавлять или удалять пару ключа и значения или обновлять значение в ключе:
val map = mapOf(1 to "one", 3 to "three", 9 to "nine")
println(map.javaClass) // class java.util.LinkedHashMap
Другой способ создания через Pair.
val map = mapOf(Pair(1, "one"), Pair(3, "three"), 9 to "nine") // оставлен один вариант через to
Вывести содержимое коллекции можно через вызов самой переменной. При этом записи словаря появляются между фигурными скобками. Ключи находятся слева от знаков равенства, а значения – справа.
println(map)
{1=one, 3=three, 9=nine}
В примере ключи были числами, а значения строками. Можно сделать наоборот, ключи будут строками, а значения числами.
val map = mapOf("One" to 1, "Two" to 2, "Eight" to 8)
// равносильно val map: Map<String, Int> = mapOf("One" to 1, "Two" to 2, "Eight" to 8)
println(map)
Получить значения всех ключей и всех значений и проделать с ними операции можно через mapKeys() и mapValues(). При этом возвращаются новые словари.
val map = mapOf(1 to "one", 3 to "three", 9 to "nine")
val keysMap = map.mapKeys{it.key.toString()}
println(keysMap) // {1=one, 3=three, 9=nine}
val valuesMap = map.mapValues{it.value.replace("e", "meow")}
println(valuesMap) // {1=onmeow, 3=thrmeowmeow, 9=ninmeow}
Проверить существование ключа или значения можно через containsKey() и containsValue().
map.containsKey("One")
Получить значение у заданного ключа можно через get() или getValue(). Если указать несуществующий ключ, то get() вернёт null, а getValue() выбросит исключение NoSuchElementException. Также можно получить значение ключа через квадратные скобки.
val map = mutableMapOf("One" to 1, "Two" to 2, "Three" to 3)
println(map.getValue("One"))
println(map["Two"])
Во избежание проблем с исключениями применяйте getOrElse() или getOrDefault().
// анонимная функция для несуществующего ключа
val map = mapOf("One" to 1, "Two" to 2, "Eight" to 8)
val number = map.getOrElse("One1") {"No such number"}
println(number)
// Значение по умолчанию
val map = mapOf("One" to 1, "Two" to 2, "Eight" to 8)
val number = map.getOrDefault("Cat", 0)
println(number)
Можно создать новый словарь на основе существующего при помощи операции withDefault(). Операция будет вызывать лямбду и возвращать результат при каждом вызове getValue() с несуществующим ключом на этом новом словаре. В этом случае отпадает надобность в getOrDefault().
val map = mapOf(1 to "one", 3 to "three", 9 to "nine")
val newMap = map.withDefault { key -> "Meow"}
println(newMap.getValue(1))
println(newMap.getValue(2)) // несуществующий ключ
Перебрать все ключи и их значения можно в цикле for.
val map = mapOf("One" to 1, "Two" to 2, "Three" to 3)
for((key, value) in map){
println("Key is $key, value is $value")
}
А можно через функцию forEach().
val map = mapOf("One" to 1, "Two" to 2, "Three" to 3)
map.forEach{entry ->
println("Значение ${entry.value} у ключа ${entry.key}")
}
Значение 1 у ключа One
Значение 2 у ключа Two
Значение 3 у ключа Three
Применим функцию отбора и преобразования.
val map = mapOf(1 to "one", 3 to "three", 9 to "nine")
println(map.mapValues { it.value.toUpperCase() })
{1=ONE, 3=THREE, 9=NINE}
Создадим класс, содержащий имена котов, их вес и описание. После этого создадим список котов. На основе списка мы можем создать словарь при помощи функции associate(). Например, можно создать словать путём соотнесения двух свойств в элементах списка (имя и описание).
class Cat(
val name: String,
val weight: Int,
val description: String
)
val cats = listOf(
Cat("Barsik", 4, "Clever"),
Cat("Murzik", 3, "Sleepy"),
Cat("Ryzhik", 5, "Player")
)
val catsMap = cats.associate{ cat ->
cat.name to cat.description
}
println(catsMap)
Существует также функция associateBy().
Изменяемый Map.
val currenciesMutableMap: MutableMap<String, String> =
mutableMapOf("Гривна" to "Украина", "Доллар" to "США", "Рубль" to "Россия")
println("Страны: ${currenciesMutableMap.values}") // Страны: [Украина, США, Россия]
println("Валюты: ${currenciesMutableMap.keys}") // Валюты: [Гривна, Доллар, Рубль]
currenciesMutableMap.put("Тугрик", "Монголия")
currenciesMutableMap.remove("Доллар")
println(currenciesMutableMap) //{Гривна=Украина, Рубль=Россия, Тугрик=Монголия}
Так как мы имеем дело с изменяемым Map, то у него есть дополнительные возможности. Например, мы можем добавить новую запись через put().
val map = mutableMapOf("One" to 1, "Two" to 2, "Three" to 3)
map.put("Four", 4)
println(map)
Можно добавить через оператор +=. Если ключ уже существует, то его значение будет переписано.
val map = mutableMapOf("One" to 1, "Two" to 2, "Three" to 3)
map.put("Four", 4)
map += "Five" to 5
map += "Two" to 22
println(map) //{One=1, Two=22, Three=3, Four=4, Five=5}
Можно сначала подготовить несколько записей для вставки и вставить их сразу через putAll():
val map = mutableMapOf("One" to 1, "Two" to 2, "Three" to 3)
val entry1 = Pair("Four", 4)
val entry2 = Pair("Five", 5)
val entryToAdd = mapOf(entry1, entry2)
map.putAll(entryToAdd)
println(map)
Объединить две коллекции можно также через putAll().
val mapA = mutableMapOf<String, Int>("a" to 1, "b" to 2)
val mapB = mutableMapOf<String, Int>("a" to 2, "d" to 4)
mapA.putAll(mapB)
println(mapA) // {a=2, b=2, d=4}
При совпадении ключей побеждает более поздний вызов, поэтому ключ из mapB перепишет ключ из mapA.
Удалить запись можно по ключу через remove():
map.remove("Two")
Перегруженная версия remove() удалит запись только при совпадении ключа и его значения.
val map = mutableMapOf("One" to 1, "Two" to 2, "Three" to 3)
val entry1 = Pair("Four", 4)
val entry2 = Pair("Five", 5)
val entryToAdd = mapOf(entry1, entry2)
map.putAll(entryToAdd)
map.remove("Two", 3) // не удалит
map.remove("Five", 5) // удалит
println(map)
Очистить все записи можно через clear(). Но сам объект остаётся.
Можно перевести в список List через toList().
val list = map.toList()
println(list)
//[(One, 1), (Two, 2), (Three, 3), (Four, 4), (Five, 5)]
Изменяемый HashMap.
//val personsHashMap: java.util.HashMap<Int, String> =
// hashMapOf(1 to "Барсик", 2 to "Мурзик", 3 to "Рыжик")
val personsHashMap = hashMapOf(1 to "Барсик", 2 to "Мурзик", 3 to "Рыжик")
personsHashMap.put(4, "Васька")
personsHashMap.remove(2)
println(personsHashMap[1])
Получить значение по ключу можно через метод getOrDefault() (относится к Java 8). Если ключ не будет найден, то подставится значение по умолчанию.
fun byName(name: String): String =
map.getOrDefault(name, "cat")
private val map: Map<String, String> = hashMapOf(
"one" to "value one",
"two" to "value two",
"three" to "value three"
)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
println(byName("one"))
// укажем несуществующий ключ
println(byName("barsik"))
}
Если вынуждены использовать Java 7, то используйте квадратные скобки и элвис-оператор.
fun byName(name: String): String = map[name] ?: "cat"
Изменяемый LinkedHashMap.
val postalHashMap: java.util.LinkedHashMap<String, String> =
linkedMapOf("NG" to "Nigeria","AU" to "Australia","CA" to "Canada")
postalHashMap.put("NA", "Namibia")
postalHashMap.remove("AU")
postalHashMap.get("CA") // Canada
Изменяемый SortedMap, сортировка по ключу.
val personsSortedMap: java.util.SortedMap<Int, String> =
sortedMapOf(2 to "Барсик", 1 to "Рыжик", 3 to "Мурзик")
personsSortedMap.put(7, "Васька")
personsSortedMap.remove(3)