이 두 인터페이스 모두 하나의 메서드 만 정의합니다.
public operator fun iterator(): Iterator<T>
문서에 따르면 Sequence
게으르다는 의미입니다. 그러나 Iterable
게으르지 Collection
않습니까 (으로 뒷받침되지 않는 한 )?
답변
의미의 차이점 거짓말하고 대한 다음 stdlib 확장 기능의 구현 Iterable<T>
과 Sequence<T>
.
-
의 경우
Sequence<T>
확장 기능은 Java Streams 중간 작업 과 유사하게 가능한 경우 느리게 수행 됩니다. 예를 들어,Sequence<T>.map { ... }
다른Sequence<R>
항목을 반환 하고 또는 같은 터미널 작업이 수행 될 때까지 항목을 실제로 처리하지 않습니다.toList
fold
이 호출 .이 코드를 고려하십시오.
val seq = sequenceOf(1, 2) val seqMapped: Sequence<Int> = seq.map { print("$it "); it * it } // intermediate print("before sum ") val sum = seqMapped.sum() // terminal
다음을 인쇄합니다.
before sum 1 2
Sequence<T>
터미널 에서 수행되는 작업을 줄이고 싶을 때 지연 사용 및 효율적인 파이프 라이닝을위한 것입니다.Java Streams와 마찬가지로 작업 작업을 최대한 . 그러나 게으름으로 인해 약간의 오버 헤드가 발생하여 작은 컬렉션의 일반적인 단순 변환에는 바람직하지 않으며 성능이 떨어집니다.일반적으로 필요한시기를 결정하는 좋은 방법이 없으므로 Kotlin에서는 stdlib 게으름이 명시 적으로 만들어 지고 기본적
Sequence<T>
으로 모든에서 사용하지 않도록 인터페이스로 추출됩니다Iterable
. -
들어
Iterable<T>
반대와 확장 기능, 중간 동작의 의미가 열심히 일을, 바로 항목을 처리하고 다른 반환Iterable
. 예를 들어, 매핑 결과와 함께Iterable<T>.map { ... }
aList<R>
를 반환 합니다.Iterable에 해당하는 코드 :
val lst = listOf(1, 2) val lstMapped: List<Int> = lst.map { print("$it "); it * it } print("before sum ") val sum = lstMapped.sum()
다음과 같이 출력됩니다.
1 2 before sum
위에서 언급했듯이
Iterable<T>
기본적으로 지연되지 않으며이 솔루션은 잘 보여줍니다. 대부분의 경우 참조 지역성 이 좋으 므로 CPU 캐시, 예측, 프리 페치 등을 활용하여 컬렉션을 여러 번 복사해도 여전히 잘 작동합니다. 작은 컬렉션이있는 간단한 경우에 더 잘 수행됩니다.평가 파이프 라인에 대한 더 많은 제어가 필요한 경우
Iterable<T>.asSequence()
기능이 있는 지연 시퀀스로의 명시 적 변환이 있습니다.
답변
핫키의 답변 완료 :
Sequence 및 Iterable이 요소 전체에서 어떻게 반복되는지 확인하는 것이 중요합니다.
시퀀스 예 :
list.asSequence().filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
로그 결과 :
필터-지도-각각; 필터-지도-각각
반복 가능한 예 :
list.filter { field ->
Log.d("Filter", "filter")
field.value > 0
}.map {
Log.d("Map", "Map")
}.forEach {
Log.d("Each", "Each")
}
필터-필터-지도-지도-각각-각각
답변
Iterable
의java.lang.Iterable
인터페이스에
매핑되고JVM
List 또는 Set과 같이 일반적으로 사용되는 컬렉션에 의해 구현됩니다. 이들에 대한 컬렉션 확장 함수는 열심히 평가됩니다. 즉, 모두 입력의 모든 요소를 즉시 처리하고 결과를 포함하는 새 컬렉션을 반환합니다.다음은 컬렉션 함수를 사용하여 나이가 21 세 이상인 목록에서 처음 5 명의 이름을 가져 오는 간단한 예입니다.
val people: List<Person> = getPeople() val allowedEntrance = people .filter { it.age >= 21 } .map { it.name } .take(5)
타겟 플랫폼 : JVMRunning on kotlin v. 1.3.61 첫째, 목록에있는 모든 사람에 대해 연령 검사가 수행되고 결과가 새로운 목록에 포함됩니다. 그런 다음 필터 연산자 뒤에 남아있는 모든 사람에 대해 이름에 대한 매핑이 수행되어 또 다른 새 목록 (이제
List<String>
)이됩니다. 마지막으로 이전 목록의 처음 5 개 요소를 포함하도록 생성 된 마지막 새 목록이 하나 있습니다.반대로 Sequence는 느리게 평가 된 값 모음을 나타내는 Kotlin의 새로운 개념입니다.
Sequence
인터페이스에 대해 동일한 컬렉션 확장을 사용할 수 있지만 이러한 확장은 실제로 요소를 처리하지 않고 처리 된 날짜 상태를 나타내는 Sequence 인스턴스를 즉시 반환합니다. 처리를 시작하려면
Sequence
터미널 운영자로를 종료해야합니다. 기본적으로 시퀀스에 대한 요청으로 구체적인 형식으로 데이터를 구체화해야합니다. 예는toList
,toSet
그리고sum
몇 가지를 언급. 이러한 항목이 호출되면 요구되는 결과를 생성하기 위해 필요한 최소 요소 수만 처리됩니다.기존 컬렉션을 시퀀스로 변환하는 것은 매우 간단하며
asSequence
확장 기능 만 사용하면 됩니다. 위에서 언급했듯이 터미널 연산자도 추가해야합니다. 그렇지 않으면 시퀀스가 처리를 수행하지 않습니다 (다시, 게으른!).
val people: List<Person> = getPeople() val allowedEntrance = people.asSequence() .filter { it.age >= 21 } .map { it.name } .take(5) .toList()
대상 플랫폼 : JVMRunning on kotlin v. 1.3.61이 경우 시퀀스의 Person 인스턴스는 각각 나이를 확인하고 통과하면 이름을 추출한 다음 결과 목록에 추가합니다. 이것은 5 명의 사람이 발견 될 때까지 원래 목록의 각 사람에 대해 반복됩니다. 이 시점에서 toList 함수는 목록을 반환하고 나머지 사람들은
Sequence
처리되지 않습니다.시퀀스가 할 수있는 추가 기능도 있습니다. 무한한 수의 항목을 포함 할 수 있습니다. 이를 관점에서 볼 때 연산자가 자신이하는 방식으로 작업하는 것이 합리적입니다. 무한 시퀀스의 연산자는 열심히 작업을 수행하면 절대로 돌아올 수 없습니다.
예를 들어, 터미널 운영자가 요구하는만큼 2의 거듭 제곱을 생성하는 시퀀스가 있습니다 (이게 빠르게 오버플로된다는 사실을 무시 함).
generateSequence(1) { n -> n * 2 } .take(20) .forEach(::println)
여기에서 더 많은 정보를 찾을 수 있습니다 .