Map: специфичные операции
В Map типы ключей и значений определяются пользователем. При этом благодаря доступу к значениям по их ключу открываются различные возможности для обработки записей: от получения значения по ключу, до фильтрации ключей и значений по отдельности.
В этом разделе находится описание функций из стандартной библиотеки для обработки ассоциативных списков.
Получение ключей и значений
Для того чтобы получить значение из Map, вы должны передать его ключ в качестве аргумента функции
get(). Также поддерживается сокращённый синтаксис - [key]. Если такой ключ не найден, то вернётся null.
Помимо этого существует функция getValue(), которая в случае отсутствия ключа бросит исключение.
Также есть еще две функции для решения проблемы отсутствия ключа:
* getOrElse() - работает так же, как и для списков: если ключ не найден, то вернётся результат выполнения лямбды.
* getOrDefault() - если ключ не найден, то вернёт заданное значение по умолчанию.
fun main() {
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3)
println(numbersMap.get("one")) // 1
println(numbersMap["one"]) // 1
println(numbersMap.getOrDefault("four", 10)) // 10
println(numbersMap["five"]) // null
//numbersMap.getValue("six") // exception!
}
Если для выполнения операций вам требуются все ключи или все значения из Map, то воспользуйтесь свойствами keys и values. keys - это набор (Set) всех ключей, а values - коллекция всех значений.
fun main() {
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3)
println(numbersMap.keys) // [one, two, three]
println(numbersMap.values) // [1, 2, 3]
}
Фильтрация
Вы можете фильтровать ассоциативный список с помощью функции
filter() как и другие коллекции. Для этого передайте filter() предикат с Pair в качестве аргумента, что позволит использовать и ключ, и значение в предикате.
fun main() {
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredMap = numbersMap.filter { (key, value) -> key.endsWith("1") && value > 10}
println(filteredMap) // {key11=11}
}
Помимо этого существует две функции фильтрации, специфичные именно для ассоциативного списка:
filterKeys() для фильтра по ключам и filterValues() для фильтра по значениям. Обе возвращают новый ассоциативный список, в котором все записи соответствуют заданному предикату. Предикат функции filterKeys() проверяет только ключи элементов, а предикат функции filterValues() проверяет только значения.
fun main() {
val numbersMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3, "key11" to 11)
val filteredKeysMap = numbersMap.filterKeys { it.endsWith("1") }
val filteredValuesMap = numbersMap.filterValues { it < 10 }
println(filteredKeysMap) // {key1=1, key11=11}
println(filteredValuesMap) // {key1=1, key2=2, key3=3}
}
Операторы plus и minus
Из-за наличия доступа к элементам по их ключам операторы
plus (+) и
minus (-) работают с ассоциативными списками иначе, чем с другими коллекциями.
Оператор plus возвращает Map, которая содержит в себе элементы обоих операндов: первым операндом должен быть Map, а вторым может быть Pair или другая Map. Если второй операнд содержит в себе записи с ключами, которые есть в первом операнде, то в результирующую Map попадут записи из второго операнда.
fun main() {
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3)
println(numbersMap + Pair("four", 4)) // {one=1, two=2, three=3, four=4}
println(numbersMap + Pair("one", 10)) // {one=10, two=2, three=3}
println(numbersMap + mapOf("five" to 5, "one" to 11)) // {one=11, two=2, three=3, five=5}
}
Оператор minus создаёт Map на основе первого операнда, исключая те записи, ключи которых есть во втором операнде. Таким образом, второй операнд может быть либо одним ключом, либо коллекцией ключей: списком, множеством и так далее.
fun main() {
val numbersMap = mapOf("one" to 1, "two" to 2, "three" to 3)
println(numbersMap - "one") // {two=2, three=3}
println(numbersMap - listOf("two", "four")) // {one=1, three=3}
}
Подробнее об использовании операторов
plusAssign (+=) и
minusAssign (-=) с изменяемыми ассоциативными списками см. ниже Операции записи.
Операции записи
Изменяемые ассоциативные списки предлагают специфичные для них операции записи. Такие операции позволяют изменять содержимое ассоциативного списка, используя доступ к значениям по ключу.
Все операции записи придерживаются следующих правил: * Значения можно обновлять. В свою очередь, ключи никогда не меняются: как только вы добавили запись, её ключ остаётся неизменным. * Для каждого ключа всегда есть одно значение, связанное с ним. Вы можете добавлять и удалять записи целиком.
Ниже описаны функции для операций записи из стандартной библиотеки, доступные для использования с изменяемыми ассоциативными списками.
Добавление и обновление записи
Для добавления в изменяемый ассоциативный список новой пары “ключ-значение” используйте функцию
put(). Когда новая запись помещается в LinkedHashMap (реализация Map по умолчанию), она добавляется в неё таким образом, что при итерации Map она отображается последней. В отсортированных ассоциативных списках положение новых элементов определяется порядком их ключей.
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap.put("three", 3)
println(numbersMap) // {one=1, two=2, three=3}
}
Для добавления нескольких записей за раз, используйте функцию
putAll(). В качестве аргумента она принимает Map или группу из Pair: Iterable, Sequence, или Array.
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
numbersMap.putAll(setOf("four" to 4, "five" to 5))
println(numbersMap) // {one=1, two=2, three=3, four=4, five=5}
}
И put(), и putAll() перезаписывают значения, если указанные ключи уже существуют в Map. Поэтому вы можете использовать их, чтобы обновить значения записей.
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
val previousValue = numbersMap.put("one", 11)
println("value associated with 'one', before: $previousValue, after: ${numbersMap["one"]}") // 11
println(numbersMap) // {one=11, two=2}
}
Помимо этого, вы можете использовать упрощённый вариант для добавления записей в ассоциативный список. Есть два способа:
* Оператор plusAssign (+=).
* [].
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2)
numbersMap["three"] = 3 // calls numbersMap.put("three", 3)
numbersMap += mapOf("four" to 4, "five" to 5)
println(numbersMap) // {one=1, two=2, three=3, four=4, five=5}
}
Если вызвать эти операторы с ключом, который уже существует в ассоциативном списке, то его значение будет перезаписано.
Удаление записей
Для удаления записи из изменяемой Map, используйте функцию
remove(). При вызове remove(), вы можете передать ей либо только ключ, либо и ключ, и его значение. При этом, если вы передадите и ключ, и значение, то элемент с этим ключом будет удалён только в том случае, если его значение совпадает с переданным значением.
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
numbersMap.remove("one")
println(numbersMap) // {two=2, three=3}
numbersMap.remove("three", 4) // ничего не удаляет
println(numbersMap) // {two=2, three=3}
}
Также вы можете удалять записи из ассоциативного списка используя свойства keys или values: просто вызовите функцию remove() для них. При вызове remove() с values, будет удалена только первая запись с таким значением.
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3, "threeAgain" to 3)
numbersMap.keys.remove("one")
println(numbersMap) // {two=2, three=3, threeAgain=3}
numbersMap.values.remove(3)
println(numbersMap) // {two=2, threeAgain=3}
}
Оператор minusAssign (-=) для изменяемых Map тоже доступен.
fun main() {
val numbersMap = mutableMapOf("one" to 1, "two" to 2, "three" to 3)
numbersMap -= "two"
println(numbersMap) // {one=1, three=3}
numbersMap -= "five" // ничего не удаляет
println(numbersMap) // {one=1, three=3}
}