실험 배경
프로그래머스의 기초 코딩 테스트 홀짝에 따라 다른 값 반환하기 문제를 풀다가 Collection 방식과 Sequence 방식의 성능 차이에 대해 궁금증이 생겼다. 이에 따라 간단한 연산부터 복잡한 연산까지 두 방식을 비교하여 어떤 상황에서 어떤 방식이 더 적합한지 알아보는 실험을 진행하였다.
실험 목적
- Collection 방식과 Sequence 방식의 성능을 비교한다.
- 간단한 연산과 복잡한 연산에서 두 방식의 성능 차이를 분석한다.
- 데이터 크기와 연산 복잡성에 따른 적합한 방식을 도출한다.
실험 환경 및 코드
코드 설명
실험에서는 Collection 방식을 사용하는 코드와 Sequence 방식을 사용하는 코드를 각각 작성하였다.
Collection 방식
fun solutionWithCollection(n: Int): Int {
return if (n % 2 == 1) {
val k = n / 2 + 1
k * k
} else {
(2..n step 2).sumOf { it * it }
}
}
Sequence 방식
fun solutionWithSequence(n: Int): Int {
return if (n % 2 == 1) {
val k = n / 2 + 1
k * k
} else {
(2..n step 2).asSequence().map { it * it }.sum()
}
}
메인 함수
fun main() {
val solution = Solution()
val testSizes = listOf(10, 100, 1_000, 10_000, 100_000, 1_000_000, 10_000_000, 100_000_000)
println("Performance Comparison (Time in nanoseconds):")
println("-------------------------------------------------------------")
println(String.format("%10s | %15s | %15s | %10s", "n", "Collection", "Sequence", "Faster"))
println("-------------------------------------------------------------")
for (n in testSizes) {
val collectionTime = measureNanoTime {
solution.solutionWithCollection(n)
}
val sequenceTime = measureNanoTime {
solution.solutionWithSequence(n)
}
val faster = if (collectionTime < sequenceTime) "Collection" else "Sequence"
println(
String.format(
"%10d | %15d | %15d | %10s",
n, collectionTime, sequenceTime, faster
)
)
}
}
실험 결과
실험 1: 간단한 연산
연산이 간단한 경우, Collection 방식이 Sequence 방식보다 빠른 성능을 보였다.

분석
- Collection 방식은 즉시 연산(Eager Evaluation)으로 데이터를 한 번에 처리하기 때문에 오버헤드가 적다.
- Sequence 방식은 지연 연산(Lazy Evaluation)으로 설정 비용과 함수 호출 오버헤드가 발생하여 작은 데이터에서는 더 느리다.
실험 2: 복잡한 연산
복잡한 연산에서는 Sequence 방식이 Collection 방식보다 빠른 성능을 보이는 경향이 있었다.
실험 코드
// Collection 방식
fun solutionWithCollection(n: Int): Int {
return if (n % 2 == 1) {
val k = n / 2 + 1
k * k
} else {
(2..n step 2)
.filter { it % 3 == 0 }
.map { it * it }
.filter { it % 5 == 0 }
.sum()
}
}
// Sequence 방식
fun solutionWithSequence(n: Int): Int {
return if (n % 2 == 1) {
val k = n / 2 + 1
k * k
} else {
(2..n step 2).asSequence()
.filter { it % 3 == 0 }
.map { it * it }
.filter { it % 5 == 0 }
.sum()
}
}
실험 결과

분석
- 중간 연산이 많을수록 Sequence 방식의 지연 평가가 빛을 발한다.
- Sequence 방식은 중간 데이터를 저장하지 않으므로 메모리를 효율적으로 사용한다.
종합 분석
연산이 단순할 때: Collection 방식이 유리하다
- 즉시 평가 (Eager Evaluation):
- sumOf는 컬렉션을 한 번 순회하며 모든 연산을 즉시 수행한다.
- 중간 데이터를 생성하지 않아 메모리 사용량이 적다.
- 함수 호출 오버헤드 없음:
- Collection 방식은 각 단계에서 함수 호출 비용이 발생하지 않는다.
연산이 복잡할 때: Sequence 방식이 유리하다
- 중간 연산이 많은 경우:
- Sequence 방식은 지연 평가(Lazy Evaluation)로 불필요한 계산을 생략한다.
- 데이터 크기가 큰 경우:
- Sequence 방식은 메모리를 효율적으로 사용하며, 중간 데이터를 저장하지 않아 성능이 높다.
최종 결론
- Collection 방식 사용 권장
- 연산이 단순하고 중간 연산이 적으며 데이터 크기가 작은 경우.
- 예: (2..n step 2).sumOf { it * it }
- Sequence 방식 사용 권장
- 연산이 복잡하거나 중간 연산이 많고 데이터 크기가 큰 경우.
- 예: (2..n step 2).asSequence().map { it * it }.sum()
- 데이터 크기 및 복잡성 고려
- 데이터 크기가 작을 때는 Collection 방식이 더 빠르며, 복잡한 연산이 많아질수록 Sequence 방식의 효율성이 커진다.
이번 실험을 통해 두 방식의 특성과 사용 시점을 명확히 이해할 수 있었다. 앞으로 문제의 특성에 따라 적합한 방식을 선택하여 더 효율적인 코드를 작성할 수 있을 것이다.