람다 이어서 공부!
people.map(Person::name).filter { it.startsWith("A") }
people.asSequence()
.map(Person::name) // 시퀀스 상태(중간 연산)
.filter { it.startsWith("A") } // 시퀀스 상태(중간 연산)
.toList() // 다시 컬렉션으로(최종 연산)
위의 연산처럼 단순히 map과 filter를 이어서 연산하면 map이 연산결과 리스트를 하나 만들고, 거기서 filter가 실행되어 리스트를 2번 만드는 셈이 된다. 전체 컬렉션에 연산을 적용하는 셈이다.
아래 연산은 people을 시퀀스로 변환한 뒤 map과 filter를 거쳐 마지막으로 리스트를 한번만 반환한다. 이를 지연 시퀀스 연산이라 하는데, 원소들이 중간 처리결과 없이 필요할 때 연쇄적으로 한번에 계산된다. 위와 달리 원소를 한번에 하나씩 처리한다.
아래의 지연 시퀀스 연산은 중간 결과를 생성하지 않기 때문에 메모리 사용량이 적고, 데이터 셋이 많을 경우 성능상 이점이 발생한다.(적으면 오히려 느릴 수도)
따라서 데이터 셋이 많으면 지연 시퀀스 연산이 유리하다.
중간연산은 항상 지연 계산된다. 최종 연산을 호출할 때 비로소 모든 계산이 수행된다.
filter를 먼저 수행해 부적절한 원소를 제외하면 연산이 더 빠른 경우도 있다.
수신 객체를 명시하지 않고 람다의 본문 안에서 다른 객체의 메서드를 호출할 수 있게 하는 것
with
함수코틀린에서 제공되는 표준 라이브러리 함수 중 하나로, 특정 객체를 사용하여 수행할 작업을 지정하는 데 사용된다. with 함수는 해당 객체의 메서드 호출이나 프로퍼티 접근을 좀 더 간결하게 표현할 수 있도록 도와준다.
수신 객체(receiver)와 람다 함수(block)를 인자로 받는다. block 내에서는 수신 객체의 메서드나 프로퍼티에 직접 접근할 수 있다.
with문은 특별한 구문이 아니라 파라미터가 2개 있는 함수라고 보면 된다.
일반적으로 수신객체는 ()로 받고, 람다는 {}로 받는다.
첫번째 인자로 받은 객체를 두번째 인자로 받은 람다의 수신 객체로 만든다.
람다 본문에선 this로 그 수신객체에 접근이 가능하다.
fun <T, R> with(receiver: T, block: T.() -> R): R {
return receiver.block()
}
data class Person(var name: String, var age: Int)
fun main() {
val person = Person("Alice", 30)
val result = with(person) {
name = "Bob"
age += 1
"The person's name is $name and age is $age."
}
println(result) // 출력: "The person's name is Bob and age is 31."
println(person) // 출력: Person(name=Bob, age=31)
}
apply
함수with와 거의 비슷하지만, 항상 자신에게 전달된 객체(수신 객체)를 반환한다는 점이 다르다.
inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
apply 함수는 수신 객체(this)를 그대로 반환하면서, 수신 객체에 대해 수행할 작업을 람다 함수(block)로 받는다. 람다 내에서는 수신 객체의 메서드나 프로퍼티에 직접 접근할 수 있다.
with 함수는 특정 객체를 수신 객체로 받지만, apply 함수는 함수를 호출하는 객체가 수신 객체가 된다.
with vs apply
// with 사용
val resultWith = with(person) {
name = "Bob"
age += 1
"Result from with"
}
// apply 사용
val resultApply = person.apply {
name = "Charlie"
age += 1
}