코틀린 (Kotlin) 컬렉션
코틀린 컬렉션
fun functionalFilter(numList : List<Int>) : List<Int> = numList.filter { it % 2 == 0 }- 코드 간결성/가독성 향상
- 결과값을 저장하기 위한 별도의 리스트를 생성할 필요가 없다
- 비즈니스 로직에 집중
- 버그가 발생할 확률이 적다
- 테스트가 용이
- 유지보수가 용이
fun functionalMap(numList: List<Int>) : List<Int> {
return numList.map { it + 2 }
}
- map 함수는 내부적으로 새로운 리스트를 만들어서 반환 -> 부수효과 없음
tailrec fun<T, R> FunList<T>.map(acc: FunList<R> = FunList.Nil, f: (T) -> R): FunList<R> = when (this) {
FunList.Nil -> acc.reverse()
is FunList.Cons -> tail.map(acc.addHead(f(head)), f)
}
- 제네릭과 고차함수를 활용해서 일반화된 함수를 만들고, 재사용성을 높이는 것이 함수적 접근 방식
명령형과 함수형의 차이
fun func(intList: List<Int>) =
intList
.map { n ->
println("map=$n")
n*n }
.filter { n ->
println("filter=$n")
n < 10 }
.first()
println(func(listOf(1,2,3,4,5))) //"1"출력
*개인적으로 코틀린을 공부하면서 정리한 자료입니다. 수정 사항 및 이슈가 있는 경우 메일 부탁드립니다.
intList
.map { n ->
println("map=$n")
n*n }
.filter { n ->
println("filter=$n")
n < 10 }
.first()
println(func(listOf(1,2,3,4,5))) //"1"출력
- map 5번 실행, filter 5번 실행, first() 를 실행하기때문에 명령형보다 반복 계산이 많을 수 있음
코틀린에서 아래 상황에서는 컬렉션을 사용하지 않는것이 좋다
- 성능에 민감할때
- 컬렉션의 크기가 고정되어 있지 않을 때
- 고정된 컬렉션 크기가 매우 클 때
코틀린의 컬렉션은 기본적으로 값이 즉시 평가되기 때문에 게으른 평가로 실행되지 않아 성능이 떨어진다.
시퀀스
fun seqFunc(intList: List<Int>) =
intList
.asSequence()
.map { n ->
println("map=$n")
n*n }
.filter { n ->
println("filter=$n")
n < 10 }
.first()
시퀀스를 사용해서 성능을 향상 시킬수 있다.
Lazy 컬렉션
- 람다로 매개변수를 받으면 Cons 생성되는 시점에 매개변수가 평가되지 않는다.
sealed class FunStream<out T> {
object Nil : FunStream<Nothing>()
data class Cons<out T>(val head: () -> T, val tail: () -> FunStream<T>) : FunStream<T>()
}
fun <T> funStreamOf(vararg elements: T): FunStream<T> = elements.toFunStream()
fun <T> Array<out T>.toFunStream(): FunStream<T> = when {
this.isEmpty() -> FunStream.Nil
else -> FunStream.Cons({ this[0] }, { this.copyOfRange(1, this.size).toFunStream() })
}
fun <T> FunStream<T>.getHead(): T = when(this) {
FunStream.Nil -> throw NoSuchElementException()
is FunStream.Cons -> head()
}
fun <T> FunStream<T>.getTail(): FunStream<T> = when(this) {
FunStream.Nil -> throw NoSuchElementException()
is FunStream.Cons -> tail()
}
- 리스트 크기가 클 때, Lazy 컬렉션처럼 사용하는 것이 성능에 좋다
무한대 값 만들기
- generateFunSream()은 함수를 입력받기때문에 즉시 평가 되지 않는다.
- 함수들은 실제로 평가될 때 어떤 일들을 해야 하는지만을 기록하고 있을뿐이다.
fun <T> generateFunStream(seed: T, generate: (T) -> T) : FunStream<T> =
FunStream.Cons({seed}, {generateFunStream(generate(seed), generate)})
tailrec fun <T> FunStream<T>.forEach(f: (T)->Unit): Unit = when(this) {
FunStream.Nil -> Unit
is FunStream.Cons -> {
f(head())
tail().forEach(f)
}
}
val infiniteVal = generateFunStream(0) {
it + 5
}
infiniteVal.forEach {
println(it)
}
댓글
댓글 쓰기