data class Person(val name: String, val age: Int)fun main() {
val list = listOf(1, 2, 3, 4)
println(list.filter { it % 2 == 0 })
}
filter 함수는 컬렉션을 순회하면서 주어진 람다가 true를 반환하는 원소들만 모은다.
filter 함수는 주어진 술어와 일치하는 원소들로 이뤄진 새 컬렉션을 만들 수 있지만 그 과정에서 원소를 변환하지는 않는다.
fun main() {
val list = listOf(1, 2, 3, 4)
println(list.map { it * it })
}
map은 입력 컬렉션의 원소를 변환할 수 있게 해준다.
fun main() {
val numbers = listOf(1, 2, 3, 4, 5, 6, 7)
val filtered = numbers.filterIndexed { index, element -> index % 2 == 0 && element > 3 }
println(filtered)
val mapped = numbers.mapIndexed { index, element -> index + element }
println(mapped)
}
index와 원소를 함께 제공
🔖 6.1.2 컬렉션 값 누적: reduce와 fold
fun main() {
val list = listOf(1, 2, 3, 4, 5, 6, 7)
println(list.reduce { acc, element -> acc + element })
}
reduce를 사용하면 컬렉션의 첫 번째 값을 누적기에 넣는다.
빈 컬렉션에 호출하면 안 된다.
fun main() {
val people = listOf(
Person("Alice", 29),
Person("Bob", 31),
)
val folded = people.fold("") { acc, person -> acc + person.name }
println(folded)
}
fold 함수는 reduce와 비슷하지만 첫 번째 원소를 누적 값으로 시작하는 대신, 임의의 시작 값을 선택할 수 있다.
runningReduce, runningFold를 사용하면 중간 단계의 모든 누적 값을 뽑아낼 수 있다.
반환객체는 리스트다.
🔖 6.1.3 컬렉션에 술어 적용: all, any, none, count, find
val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
val people = listOf(
Person("Alice", 27),
Person("Bob", 31),
)
println(people.all(canBeInClub27))
}
all은 모든 원소가 술어를 만족시키는지 판단한다.
any는 술어를 만족하는 원소가 하나라도 있는지 판단한다.
none은 술어를 만족하는 원소가 없는지를 판단한다.
val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
val people = listOf(
Person("Alice", 27),
Person("Bob", 31),
)
println(people.count(canBeInClub27))
}
술어를 만족하는 원소의 개수를 알고 싶다면 count를 사용한다.
size보다는 count가 효율적이다.
count는 개수만 추적할 뿐 객체 생성하지 않음
val canBeInClub27 = { p: Person -> p.age <= 27 }
fun main() {
val people = listOf(
Person("Alice", 27),
Person("Bob", 31),
)
println(people.find(canBeInClub27))
}
술어를 만족하는 원소를 하나 찾고 싶으면 find 함수 사용
원소가 전혀 없는 경우 null 반환
🔖 6.1.4 리스트를 분할해 리스트의 쌍으로 만들기: partition
fun main() {
val people = listOf(
Person("Alice", 26),
Person("Bob", 29),
Person("Carol", 31),
)
val comeIn = people.filter(canBeInClub27)
val stayOut = people.filterNot(canBeInClub27)
println(comeIn)
println(stayOut)
}
위 로직을 더 간결하게 처리할 수 있다.
val (comeIn, stayOut) = people.partition(canBeInClub27)
println(comeIn)
println(stayOut)
partition 함수는 컬렉션을 술어를 만족하는 그룹과 만족하지 않는 그룹으로 나눈다.
🔖 6.1.5 리스트를 여러 그룹으로 이뤄진 맵으로 바꾸기: groupBy
fun main() {
val people = listOf(
Person("Alice", 31),
Person("Bob", 29),
Person("Carol", 31),
)
println(people.groupBy { it.age })
}
zip 함수를 사용해 두 컬렉션에서 같은 인덱스에 있는 원소들의 쌍으로 이뤄진 리스트를 만들 수 있다.
결과 컬렉션의 길이는 더 짧은 쪽의 길이와 같다.
fun main() {
val countries = listOf("DE", "NL", "US")
println(names zip ages zip countries)
// [((Joe, 22), DE), ((Mary, 31), NL), ((Jamie, 22), US)]
}
중위 표기법을 쓸 수 있다.
연쇄 호출이 가능하고, 리스트의 리스트가 되지는 않는다.
🔖 6.1.11 내포된 컬렉션의 원소 처리: flatMap과 flatten
class Book(val title: String, val authors: List<String>)
val library = listOf(
Book("Kotlin in Action", listOf("Isakova", "Elizarov", "Aigner", "Jemerov")),
Book("Atomic Kotlin", listOf("Eckel", "Isakova")),
Book("The Three-Body Problem", listOf("Liu"))
)fun main() {
val authors = library.map { it.authors }
println(authors)
// [[Isakova, Elizarov, Aigner, Jemerov], [Eckel, Isakova], [Liu]]
}
people
.asSequence()
.map(Person::name)
.filter { it.startsWith("A") }
.toList()
중간 결과를 저장하는 컬렉션이 생기지 않기 때문에 원소가 많은 경우 성능이 눈에 띄게 좋아진다.
시퀀스의 원소는 필요할 때 lazy 계산이 된다.
🔖 6.2.1 시퀀스 연산 실행: 중간 연산과 최종 연산
중간 연산은 다른 시퀀스를 반환하고, 지연 계산된다.
최종 연산은 결과를 반환한다.
fun main() {
listOf(1, 2, 3, 4)
.asSequence()
.map {
print("map($it) ")
it * it
}
.filter {
print("filter($it) ")
it % 2 == 0
}
}
이 코드는 아무 내용도 출력되지 않는다.
fun main() {
listOf(1, 2, 3, 4)
.asSequence()
.map {
print("map($it) ")
it * it
}
.filter {
print("filter($it) ")
it % 2 == 0
}.toList()
}
최종 연산을 호출하면 연기됐던 모든 계산이 수행된다.
fun main() {
println(
listOf(1, 2, 3, 4)
.asSequence()
.map { it * it }
.find { it > 3 }
)
// 4
}
시퀀스에서 연산순서는 각 원소에 대해 순차적으로 적용된다.
원소에 연산을 차례대로 적용하다가 결과가 얻어지면 그 이후의 원소에 대해서는 변환이 이뤄지지 않을 수도 있다는 것
즉시 계산은 전체 컬렉션에 연산을 적용시키지만 지연 계산은 원소를 한번에 하나씩 처리한다.
fun main() {
val people = listOf(
Person("Alice", 29), Person("Bob", 31),
Person("Charles", 31), Person("Dan", 21)
)
println(
people
.asSequence()
.map(Person::name)
.filter { it.length < 4 }
.toList()
)
// [Bob, Dan]
println(
people
.asSequence()
.filter { it.name.length < 4 }
.map(Person::name)
.toList())
// [Bob, Dan]
}
연쇄적인 연산에서 더 빨리 원소들을 제거하면 할수록 코드의 성능이 좋아진다.
🔖 6.2.2 시퀀스 만들기
fun main() {
val naturalNumbers = generateSequence(0) { it + 1 }
val numbersTo100 = naturalNumbers.takeWhile { it <= 100 }
println(numbersTo100.sum())
// 5050
}
generateSequence 함수는 이전의 원소를 인자로 받아 다음 원소를 계산한다.
위 변수는 모두 지연 계산된다.
import java.io.File
fun File.isInsideHiddenDirectory() =
generateSequence(this) { it.parentFile }.any { it.isHidden }
val file = File("/Users/svtk/.HiddenDir/a.txt")
println(file.isInsideHiddenDirectory())
// true