이펙티브 코틀린(8)
8-1. 하나 이상의 처리 단계를 가진 경우에는 시퀀스를 사용하라
•
Iterable 과 Sequence 는 완전히 다른 목적으로 설계되어서, 완전히 다른 형태로 동작한다
◦
Sequence 는 지연(Lazy) 처리한다
▪
시퀀스 처리 함수들을 사용하면, 데코레이터 패턴으로 꾸며진 새로운 시퀀스가 반환된다
▪
최종 계산은 toList 또는 count 등의 최종 연산이 이루어질 때 수행된다
◦
Iterable 은 처리 함수를 사용할 때 마다 연산이 바로 이루어지면서 List 를 반환한다
예시 코드
•
Sequence 의 장점
◦
자연스러운 처리 순서 유지
◦
최소한의 연산
◦
무한 시퀀스 형태로 사용 가능
◦
각 단계에서 컬렉션을 생성하지 않음
순서의 중요성
•
Sequence 처리는 요소당 하나씩 지정한 연산을 모두 적용한다. ( 이를 element-by-element order, lazy order 라고 부른다 )
•
Iterable 은 요소 전체를 대상으로 연산을 적용한다. ( 이를 step-by-step order, eager order 라고 부른다 )
예시 코드
최소 연산
•
Iterable 은 중간 연산이라는 개념이 없기 때문에, 전체 연산을 다 끝낸 후 요소의 개수를 가져와야 한다
•
Sequence 는 중간 연산이라는 개념이 있기 때문에 원하는 요소만 가져와서 원하는 처리를 할 수 있다
예시 코드
•
중간 처리 단계를 모든 요소에 적용할 필요가 없는 경우에는 Sequence 를 사용하는 것이 좋다
무한 시퀀스
•
Sequence 는 최종 연산이 일어나기 전까지는 컬렉션에 어떠한 처리도 하지 않는다
◦
무한 시퀀스 ( Infinite Sequence ) 를 만들고, 필요한 부분까지만 값을 추출하는 것이 가능하다
◦
무한 시퀀스를 만드는 방법은 generateSequence 또는 sequence 를 사용하면 된다
•
무한 시퀀스는 값을 몇 개 활용할지 결정해야 하며, 결정하지 않은 경우에는 무한 반복을 하게 된다
예시 코드
각각의 단계에서 컬렉션을 만들어내지 않음
•
컬렉션 처리 함수는 각각의 단계에서 컬렉션을 만들어진 결과를 활용하거나 저장할 수 있다는 장점이 있지만, 공간을 차지한다는 비용이 든다는 단점이 있다
예시 코드
•
일반적으로 파일을 처리할 때 Sequence 를 활용한다
•
Sequence 를 활용하면 메모리 절약 뿐만 아니라 성능도 향상시킬 수 있다
시퀀스가 빠르지 않은 경우
•
컬렉션 전체를 기반으로 처리해야 하는 연산은 Sequence 를 사용해도 빠르지 않다
◦
e.g) stdlib 의 sorted 함수
▪
sorted 는 Sequence 를 List 로 변환한 뒤 자바 stdlib 의 sort 를 사용해서 처리한다
•
이러한 변환 처리로 인해서 Sequence 가 Collection 처리보다 느려진다
▪
무한 시퀀스에서는 sorted 를 사용하면 무한 반복에 걸릴 수 있기 때문에 조심해야 한다
•
일반적인 경우에서는 Collection 보다 Sequence 가 더 빠르다
자바 스트림의 경우
•
자바8 부터는 Collection 처리를 위해 Stream 기능이 추가되었다
◦
코틀린의 Sequence 와 비슷한 형태로 동작한다
◦
Stream 도 Lazy 하게 동작하며, 마지막 처리 단계에서 연산이 일어난다
•
Sequence 와 차이점
1.
코틀린의 Sequence 가 더 많은 처리 함수를 가지고 있다
a.
확장 함수를 사용해서 정의하기 때문이다
b.
사용하기 쉽다
2.
자바 Stream 은 병렬 함수를 사용해서 병렬 모드로 실행할 수 있다
a.
멀티 코어 환경에서 굉장히 큰 성능 향상을 가져온다
3.
코틀린의 Sequence 는 코틀린/JVM, 코틀린/JS, 코틀린/네이티브 등의 일반적인 모듈에서 모두 사용할 수 있다
a.
자바 Stream 은 코틀린/JVM 에서만 동작한다.
•
일반적으로 병렬 모드를 사용하지 않는다면, 코틀린 Sequence 를 사용하는게 좋다
코틀린 시퀀스의 디버깅
•
Kotlin Sequence Debugger 를 사용하면 어떻게 동작하는지 UI 로 확인할 수 있다
정리
•
Collection 과 Sequence 는 같은 처리 메서드를 지원하며, 사용하는 형태가 거의 비슷하다
•
일반적으로 데이터를 컬렉션에 저장하므로, Sequence 처리를 하려면 Sequence 로 변환하는 과정이 필요하다
•
Sequence 는 lazy 하게 처리된다
•
무거운 객체나 규모가 큰 Collection 을 여러 단계에 걸쳐서 처리할 대는 Sequence 를 사용하는 것이 좋다
8-2. 컬렉션 처리 단계 수를 제한하라
•
모든 Collection 처리 메서드는 비용이 많이 들어가는 작업이다
◦
따라서 적절한 메서드를 활용해서, 컬렉션 처리 단계 수를 적절하게 제한하는 것이 좋다
예시 코드
정리
•
대부분의 컬렉션 처리 단계는 전체 컬렉션에 대한 반복 과 중간 컬렉션 생성 이라는 비용이 발생한다
•
이 비용은 적절한 컬렉션 처리 함수들을 활용해서 줄일 수 있다
8-3. 성능이 중요한 부분에는 기본 자료형 배열을 사용하라
•
코틀린은 기본 자료형 (Primitive) 을 선언할 수 없지만, 최적화를 위해서 내부적으로는 사용할 수 있다
•
기본 자료형의 특징
◦
가볍다 ( 일반적인 객체와 다르게 추가적으로 포함된 기능들이 없기 때문이다 )
◦
빠르다 ( 값에 접근할 때 추가 비용이 들지 않는다 )
•
대규모의 데이터를 처리할 때 기본 자료형을 사용하면 상당히 큰 최적화를 할 수 있다
코틀린 타입 | 자바 타입 |
Int | Int |
List<Int> | List<Integer> |
Array<Int> | Integer[] |
IntArray | int[] |
•
기본 자료형 배열의 차이
◦
IntArray vs List<Int>
▪
메모리
•
단순 할당 되는 영역만 생각해보면 IntArray 는 400,000,016 Bytes , List<Int> 는 2,000,006,944 Bytes 를 할당한다
•
5배 정도의 차이가 발생한다
▪
성능
•
1,000,000개의 숫자를 갖는 컬렉션을 사용해서 평균을 구하는 처리를 해보면 약 25% 정도 성능 IntArray 가 더 빠르다
•
기본 자료형을 폼하나는 배열은 코드 성능이 중요한 부분을 최적화할 때 활용하면 좋다
◦
하지만, 일반적인 경우에는 List 를 사용하는것이 더 좋다
▪
훨씬 더 기능이 다양하고, 더 많은 곳에서 쉽게 사용할 수 있기 때문이다
정리
•
일반적으로 Array 보다 List, Set 을 사용하는게 더 좋다
•
기본 자료형의 컬렉션을 굉장히 많이 가지고 있는 상황에서 성능을 높이고 메모리 사용량을 줄이려고 할 경우에는 Array 를 사용하는것이 좋다
8-4. mutable 컬렉션 사용을 고려하라
•
immutable collection 보다 mutable collection 이 성능적으로 더 빠르다
◦
immutable collection 에서는 요소를 추가하려면 collection 을 복제해서 처리해야 하기 때문이다
구현 코드
•
mutable collection 이 성능적 관점에서 더 좋지만, immutable collection 이 안정성 측면에서 더 좋다
•
일반적인 지역 변수는 동기화와 캡슐화 문제가 없기 때문에, 지역 변수로 사용할 때는 mutable collection 을 사용하는 것이 더 합리적이다
정리
•
mutable collection 은 일반적으로 추가 처리가 빠르다
•
immutable collection 은 collection의 변경과 관련된 처리를 더 세부적으로 조정할 수 있다
•
지역 스코프에서는 세부적인 조정이 필요하지 않으므로, mutable collection 을 사용하는 것이 좋다
◦
특히, utils 에서 요소 삽입이 자주 발생할 수 있기 때문이다