5. Начните работу с фильтрами
В этом задании вы немного узнаете о фильтрах в Kotlin. Фильтры — это удобный способ получить часть списка на основе определенного условия.
Шаг 1: Создайте фильтр
- В файле Hello.kt определите список декораций для аквариума на верхнем уровне с помощью
listOf(). Вы можете заменить содержимое файла Hello.kt .
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
- Создайте новую
main()функцию со строкой кода для вывода только тех декораций, которые начинаются с буквы «p». Код условия фильтрации заключен в фигурные скобки{}иitнеявно ссылается на каждый элемент по мере того, как фильтр проходит по списку. Если выражение возвращает truetrue, элемент включается в список.
fun main() {
println( decorations.filter {it[0] == 'p'})
}
- Запустите программу, и в окне «Выполнить» вы увидите следующий вывод :
⇒ [pagoda, plastic plant]
Шаг 2: Сравните фильтры с активным и ленивым режимом фильтрации.
Если вы знакомы с фильтрами в других языках, вас может заинтересовать вопрос, являются ли фильтры в Kotlin «жадными» или «ленивыми» . Создается ли список результатов немедленно (жадный) или при обращении к нему (ленивый)? В Kotlin это происходит так, как вам нужно. По умолчанию filterиспользуется жадный фильтр, и каждый раз при использовании фильтра создается список.
Чтобы сделать фильтр ленивым, можно использовать Sequenceколлекцию, которая может просматривать только один элемент за раз, начиная с начала и заканчивая концом. Удобно, что это именно тот API, который нужен ленивому фильтру.
- В файле Hello.kt измените свой код так, чтобы отфильтрованный список был присвоен переменной с именем
eager, а затем выведите её на экран.
fun main() {
val decorations = listOf ("rock", "pagoda", "plastic plant", "alligator", "flowerpot")
// eager, creates a new list
val eager = decorations.filter { it [0] == 'p' }
println("eager: $eager")
- Ниже приведен код, который оценивает фильтр, используя оператор `
Sequencewith`asSequence(). Присвойте полученную последовательность переменной с именем `filtered` и распечатайте ее.
// lazy, will wait until asked to evaluate
val filtered = decorations.asSequence().filter { it[0] == 'p' }
println("filtered: $filtered")
Когда вы возвращаете результаты фильтрации в виде списка Sequence, filteredпеременная не будет содержать новый список — она будет содержать список Sequenceэлементов и информацию о фильтре, который нужно применить к этим элементам. При каждом обращении к элементам списка Sequenceприменяется фильтр, и результат возвращается вам.
- Принудительное вычисление последовательности путем ее преобразования в объект
Listс помощью оператораtoList()`.`. Вывести результат.
// force evaluation of the lazy list
val newList = filtered.toList()
println("new list: $newList")
- Запустите свою программу и посмотрите на результат.
⇒ eager: [pagoda, plastic plant] filtered: kotlin.sequences.FilteringSequence@386cc1c4 new list: [pagoda, plastic plant]
Для визуализации происходящего с Sequenceленивой оценкой используйте map()функцию. Эта map()функция выполняет простое преобразование каждого элемента в последовательности.
- Используя тот же
decorationsсписок, что и выше, выполните преобразование, котороеmap()ничего не делает и просто возвращает переданный элемент. Добавьте индикатор,println()который будет отображаться каждый раз при обращении к элементу, и присвойте последовательность переменной с именемlazyMap.
val lazyMap = decorations.asSequence().map {
println("access: $it")
it
}
- Выведите на экран
lazyMapпервый элемент ,lazyMapиспользуя оператор printfirst(), и выведитеlazyMapпреобразованный вList.
println("lazy: $lazyMap")
println("-----")
println("first: ${lazyMap.first()}")
println("-----")
println("all: ${lazyMap.toList()}")
- Запустите свою программу и понаблюдайте за выводом. При выводе на экран
lazyMapпросто печатается ссылка на элементSequence— внутренний методprintln()не вызывается. При выводе первого элемента осуществляется доступ только к первому элементу. Преобразование элементаSequenceв другой тип данныхListобеспечивает доступ ко всем элементам.
⇒ lazy: kotlin.sequences.TransformingSequence@5ba23b66 ----- access: rock first: rock ----- access: rock access: pagoda access: plastic plant access: alligator access: flowerpot all: [rock, pagoda, plastic plant, alligator, flowerpot]
- Создайте новый фильтр
Sequence, используя исходный фильтр, прежде чем применять егоmap. Распечатайте полученный результат.
val lazyMap2 = decorations.asSequence().filter {it[0] == 'p'}.map {
println("access: $it")
it
}
println("-----")
println("filtered: ${lazyMap2.toList()}")
- Запустите программу и понаблюдайте за дополнительным выводом. Как и в случае с получением первого элемента, внутренний метод
println()вызывается только для тех элементов, к которым осуществляется доступ.
⇒ ----- access: pagoda access: plastic plant filtered: [pagoda, plastic plant]
- Ещё одна полезная функция преобразования коллекций в Kotlin — это
flatten(). Эта функция создаёт список из коллекции коллекций, например, массива массивов или списка списков. - Создайте список списков. Затем примените функцию flatten() для преобразования всех списков в один список. Выведите результат.
val mysports = listOf("basketball", "fishing", "running")
val myplayers = listOf("LeBron James", "Ernest Hemingway", "Usain Bolt")
val mycities = listOf("Los Angeles", "Chicago", "Jamaica")
val mylist = listOf(mysports, myplayers, mycities) // list of lists
println("-----")
println("Flat: ${mylist.flatten()}")
⇒ ----- Flat: [basketball, fishing, running, LeBron James, Ernest Hemingway, Usain Bolt, Los Angeles, Chicago, Jamaica]