람다가 가독성을 올려주는 이유 중의 하나는 컬렉션에 대한 연산을 한 번에 수행할 수 있어서이다. 이미 살펴본 map, filter, any, foreach 와 같은 것들도 이러한 연산을 해 주는 것들 중 하나이다.
우선 list 를 만드는 방법 부터 살펴보자
fun createList() {
val list1 = List(10) { it }
println(list1)
val list2 = List(10) { 0 }
println(list2)
val list3 = List(10) { 'a' + it }
println(list3)
val list4 = List(10) { list3[it%3] }
println(list4)
val mutableList1 = MutableList(5, { 10 * (it + 1) })
println(mutableList1)
val mutableList2 = MutableList(5) { 10 * (it + 1) }
println(mutableList2)
}
list를 몇 개 만들지에 대한 파라미터를 넘긴 후 술부(predicate)에는 어떤 방식으로 초기화 할지를 전달하게 된다.
mutable list도 마찬가지이다. 두 번째 파라미터인 init은 별도로 빼서 람다 형식으로 적어도 된다.
이렇게 만들어진 list들에 적용할 수 있는 람다 함수를 하나씩 테스트 해 볼 수 있다.
fun predicate() {
val list = listOf(-3, -1, 5, 7, 10)
println(list.filter { it > 0 })
println(list.count { it > 0 })
println(list.find { it > 0 })
println(list.firstOrNull { it > 0})
println(list.lastOrNull { it < 0})
println(list.any { it > 0 })
println(list.any { it != 0})
println(list.all { it > 0 })
println(list.all { it != 0 })
println(list.none { it > 0 })
println(list.none { it == 0 })
}
filter, count는 조건에 맞는 것만 추출하고, find, firstOrNull은 조건에 맞는 첫 번째 값을 리턴한다. find는 찾는 값이 없을 경우 예외를 발생시키지만 firstOrNull은 값이 없을 경우 null을 반환한다.
firstOrNull과 반대되는 것은 lstOrNull 이다. 마지막 값을 찾아낸다.
any는 주어진 컬렉션에서 해당 되는 값이 한 개라도 있으면 true를, all은 모든 원소가 해당 되면 true를, none은 한 개의 원소도 해당되지 않으면 true를 리턴한다.
filter의 반대 개념으로 filterNot을 쓸 수 있다.
fun partition() {
val list = listOf(-3, -1, 5, 7, 10)
val isPositive = { i:Int -> i > 0 }
val positiveResult = list.filter(isPositive)
val negativeResult = list.filterNot(isPositive)
println(positiveResult)
println(negativeResult)
val (pos, neg) = list.partition(isPositive)
println(pos)
println(neg)
}
isPositive라는 predicate를 만들고 filter, filterNot으로 두 가지 값을 받아 출력해 보면 양수, 음수가 잘 출력 되는 것을 볼 수 있다.
이러한 경우 partition 이라는 함수로 Pair 쌍을 값으로 받을 수 있다. 위의 코드에서 해당 내용을 확인해 볼 수 있다. 결과는 아래와 같다.
null인 것을 제외하고 리턴해 주는 이미 잘 만들어진 filter도 있다. 다음과 같이 사용하면 된다.
fun filterNotNull() {
val list = listOf(1, 2, null)
println(list.filterNotNull())
}
리스트의 원소가 숫자일 경우 사용할 수 있는 sum, sorted와 같음 함수가 있지만 숫자가 아닌 경우에는 sumBy, sortedBy와 같은 함수를 사용할 수 있다.
fun byOperation() {
val products = listOf(
Product("bread", 2.0),
Product("wine", 5.0)
)
val sumByResult = products.sumByDouble { it.price }
val sumOfresult = products.sumOf { it.price }
println(sumByResult)
println(sumOfresult)
val sortedByDescendingResult = products.sortedByDescending { it.price }
println(sortedByDescendingResult)
val minByOrNullResult = products.minByOrNull { it.price }
println(minByOrNullResult)
}
결과는 아래와 같다.
sum 또는 sort를 Product의 price 필드를 보고 결정하도록 만들었다.
다만 sumBy 함수는 deprecated 되어 있다. overflow가 발생해서 문제가 발생하기 때문이다. 따라서 이런 걱정을 안하고 사용하려면 sumOf를 사용할 수 있다.
list 안에 있는 값을 취하거나 버릴 경우에는 take, drop을 쓸 수 있다. take는 조건에 맞는 것을 취하게 되고 drop은 조건에 맞는 것을 버리게 된다.
fun takeOrDrop() {
val list = listOf('a', 'b', 'c', 'X', 'Z')
val takeLastResult = list.takeLast(3)
println(takeLastResult)
val takeWhileResult = list.takeLastWhile { it.isUpperCase() }
println(takeWhileResult)
val dropResult = list.drop(1)
println(dropResult)
val dropWhileResult = list.dropWhile { it.isLowerCase() }
}
결과는 아래와 같다.
다음의 경우에는 Set에도 적용할 수 있다.
fun setOperation() {
val set = setOf("a", "ab", "ac")
val length = set.maxByOrNull { it.length }?.length
println(length)
val filterResult = set.filter { it.contains('b') }
println(filterResult)
}
maxOrNull은 null을 반환할 수 있으므로 lenght를 구하기 전에 ?를 붙여 준다.
filter, map을 set에 적용할 경우 결과값은 List가 되게 된다.
'Develop! > Kotlin' 카테고리의 다른 글
고차함수, 리스트 조작 in Kotlin (0) | 2023.10.04 |
---|---|
멤버참조 in Kotlin (0) | 2023.09.27 |
람다 in Kotlin (0) | 2023.09.25 |
제네릭스 in Kotlin (0) | 2023.09.22 |
안전한 호출과 엘비스 연산자 in kotlin (0) | 2023.09.14 |