반응형
Notice
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | |||||
3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 14 | 15 | 16 |
17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | 25 | 26 | 27 | 28 | 29 | 30 |
31 |
Tags
- 딥러닝
- 컴퓨터비전
- 클라우드컴퓨팅
- 프로그래밍
- springboot
- 자바스크립트
- 웹개발
- 컴퓨터공학
- 네트워크
- 데이터과학
- 빅데이터
- 버전관리
- 소프트웨어
- 네트워크보안
- 데이터베이스
- 자료구조
- 사이버보안
- Yes
- 보안
- 컴퓨터과학
- I'm Sorry
- 소프트웨어공학
- 프로그래밍언어
- 인공지능
- 데이터분석
- 머신러닝
- 파이썬
- 디자인패턴
- 알고리즘
- 데이터구조
Archives
- Today
- Total
스택큐힙리스트
Kotlin Sequence 내부 구현으로 배우는 Lazy Iterator 원리 본문
반응형
1. Sequence가 List·Set과 다른 진짜 이유
Sequence<T>는 데이터를 담지 않고 “필요할 때 만들어” 내보냅니다. filter → map → take 같은 여러 단계도 요소당 한 번씩 흐르기 때문에 중간 컬렉션이 전혀 생기지 않죠. Kotlin 공식 문서가 강조하듯, 이는 eager 컬렉션과 구조적으로 다릅니다.
한마디로: 이터레이터를 감춘 파이프라인.
2. 파이프라인 안쪽: TransformingSequence · FilteringSequence
- 모든 중간 연산(map / filter 등)은 새 Sequence 구현체를 리턴합니다.
- map()을 호출하면 TransformingSequence가 생성되어 원본 sequence와 lambda를 보관만 합니다.
- 실제 변환은 내부 Iterator의 next()가 불릴 때 실행됩니다.
public fun <T, R> Sequence<T>.map(
transform: (T) -> R
): Sequence<R> = TransformingSequence(this, transform)
TransformingSequence.iterator()는 원본 iterator를 들고 있다가, next()가 호출되면
transformer(it)로 값을 가공해 넘겨줍니다. 즉 “pull” 모델이라 불필요한 요소는 건드리지 않습니다.
3. 진짜 Lazy의 비밀 — SequenceBuilderIterator와 코루틴
sequence { … } 빌더 안에서 yield()를 쓰면 경량 코루틴이 생성됩니다.
SequenceBuilderIterator가 Iterator + SequenceScope를 함께 구현하고,
yield() 시점에서 suspend → 소비자에게 값 반환 → 다음 next() 때 이어서 실행 흐름을 만듭니다.
이 덕분에 무한 시퀀스도 메모리 걱정 없이 다룰 수 있죠.
4. 중간 vs 종단 연산 – 호출 순간이 다르다
- Intermediate: filter, map, distinct … → 또 다른 Sequence 반환, 아직 실행 안 됨.
- Terminal: first, toList, sum … → 그제서야 iterator 루프가 돌며 모든 연산 수행.
이 디자인 덕분에 take(1) 같은 종단 연산이 앞쪽에 오면 필요한 만큼만 작업하고 멈춥니다.
5. 퍼포먼스·메모리 관점 팁
- 작은 컬렉션 + 1~2 단계라면 일반 List 연산이 인라인 최적화 덕분에 더 빠를 수도.
- 체인이 길거나 데이터가 크면 Sequence로 중간 객체 할당을 0으로 줄이는 게 유리.
- Sequence마다 객체를 하나씩 만들기 때문에 람다 캡처가 많은 경우에는 GC 압박이 생길 수 있다.
- 일부 Sequence(예: iterator {} 빌더)는 1회 순회 전제이므로, 재사용하려면 .toList() 등으로 복사하거나 별도 시퀀스를 새로 만들어야 한다.
6. 알아두면 좋은 내부 클래스 스냅샷
- TransformingSequence – map·flatMap류
- FilteringSequence – filter·takeWhile류
- FlatteningSequence – flatMap 내부에서 다시 iterator 뽑을 때 사용
- ConstrainedOnceSequence – “한 번만 돌 수 있다”는 계약을 명시
- SequenceBuilderIterator – sequence {} 코루틴 빌더의 핵심
이들은 모두 Iterator 구현체를 생성할 때 로직을 실행하므로, JVM JIT가 인라이닝 최적화를 쉽게 적용할 수 있다는 장점도 함께 가져갑니다.
7. 실전 코드 패턴
// 기존 리스트를 지연 평가 파이프라인으로
val lazy = listOf(1, 2, 3, 4, 5)
.asSequence()
.filter { it % 2 == 1 } // 아직 실행 안 됨
.map { it * it }
println(lazy.first()) // 1만 계산
println(lazy.sum()) // 남은 9 + 25 계산
first() 호출 직후 파이프라인이 끊기므로 1·9·25 세 값만 가공했다는 점이 핵심!
8. 마무리 – 언제 Sequence를 쓸까?
상황 | 추천 |
대량 데이터 스트림, 무한 스트림 | ✅ Sequence |
데이터 적고 연산 단계도 1~2개 | 🟡 컬렉션이 더 빠를 수도 |
연산 순서를 최적으로 배치해 필터 → take 처럼 일찍 끊기고 싶을 때 | ✅ Sequence |
반응형
'개발' 카테고리의 다른 글
Kotlin also 한눈에 이해하기 (1) | 2025.07.29 |
---|---|
Mediator 패턴: 복잡한 객체 의존을 한 방에 정리하기 (2) | 2025.07.29 |
Iterator 패턴 – 컬렉션 속을 우아하게 누비는 비결 (3) | 2025.07.29 |
책임 연쇄 vs 데코레이터 — 헷갈리는 두 패턴, 결정적 차이를 콕 짚다 (1) | 2025.07.28 |
책임을 넘겨라! Chain of Responsibility 한방 정복 (2) | 2025.07.28 |
Comments