본문 바로가기

분류 전체보기

(62)
[Effective Kotlin] Item No.52 mutable 컬렉션 사용을 고려하라 immutable 컬렉션보다 mutable 컬렉션이 좋은 점은 성능 면에서 더 빠르다는 것이다. immutable 컬렉션에 요소를 추가하려면 새 컬렉션을 만들면서 여기에 요소를 추가해야 한다. 컬렉션을 복제하는 처리는 비용이 굉장히 많이 드는 처리다. 그래서 이런 복제 처리를 하지 않는 mutable 컬렉션이 성능적 관점에서 좋다. 다만 immutable 컬렉션은 안전하다는 측면에서 좋다. 따라서 지역 변수로 사용할 때는 mutable 컬렉션을 사용하는 게 더 합리적이라고 할 수 있다. 그래서 표준 라이브러리도 내부적으로 어떤 처리를 할 때는 mutable 컬렉션을 쓰도록 구현돼 있다
[Effective Kotlin] Item No.51 성능이 중요한 부분에는 기본 자료형 배열을 사용하라 코틀린은 기본 자료형(primitive)을 선언할 수 없지만 최적화를 위해 내부적으론 쓸 수 있다. 기본 자료형은 아래 특징이 있다. 일반적인 객체와 달리 추가적으로 포함되는 것들이 없기 때문에 가볍다 값에 접근할 때 추가 비용이 들어가지 않아서 빠르다 따라서 대규모 데이터 처리 시 기본 자료형을 쓰면 상당히 큰 최적화가 이뤄진다. 그런데 코틀린의 List, Set 등의 컬렉션은 제네릭 타입이다. 제네릭 타입에는 기본 자료형을 쓸 수 없으므로 wrap된 타입을 써야 한다. 일반적인 경우에는 이렇게 하는 게 훨씬 처리가 쉬워져서 적합하다. 하지만 성능이 중요한 코드라면 IntArray, LongArray 등의 기본 자료형을 활용하는 배열을 쓰는 게 좋다. 이처럼 기본 자료형을 포함하는 배열은 코드 성능이 ..
[Effective Kotlin] Item No.50 컬렉션 처리 단계 수를 제한하라 모든 컬렉션 처리 메서드는 비용이 많이 든다. 표준 컬렉션 처리는 내부적으로 요소들을 활용해 반복을 도는데 내부적으로 계산을 위해 추가적인 컬렉션도 만들어 사용한다. 따라서 적절한 메서드를 활용해서 컬렉션 처리 단계 수를 적절하게 줄이는게 좋다 class Student(val name: String?) fun List.getNames(): List = this .map { it.name } .filter { it != null } .map { it!! } fun List.getNames(): List = this .map { it.name } .filterNotNull() // Best Case fun List.getNames(): List = this .mapNotNull { it.name } 보통 첫..
[Effective Kotlin] Item No.49 하나 이상의 처리 단계를 가진 경우에는 시퀀스를 사용하라 많은 사람이 Iterable, Sequence의 차이를 잊어버린다. 사실 정의가 거의 동일해서 충분히 이해할 수 있는 일이다. interface Iterable { operator fun iterator(): Iterator } interface Sequence { operator fun iterator(): Iterator } 둘은 완전히 다른 목적으로 설계되서 완전히 다른 형태로 동작한다. Sequence는 지연처리된다. 따라서 시퀀스 처리 함수들을 쓰면 데코레이터 패턴으로 꾸며진 새 시퀀스가 리턴된다. 최종 계산은 toList 또는 count 등의 최종 연산이 이뤄질 때 수행된다. 반면 Iterable은 처리 함수를 쓸 때마다 연산이 이뤄져 List가 만들어진다. 정리하면 컬렉션 처리 연산은 호출 ..
[Effective Kotlin] Item No.48 더 이상 쓰지 않는 객체의 레퍼런스를 제거하라 자바는 가비지 컬렉터가 객체 해제와 관련된 모든 작업을 해 준다. 그렇다고 메모리 관리를 완전히 무시하면 메모리 누수가 발생해서 상황에 따라 OOM이 발생하기도 한다. 따라서 더 이상 쓰지 않는 객체의 레퍼런스를 유지하면 안 된다는 규칙 정도는 지키는 게 좋다. 특히 어떤 객체가 메모리를 많이 차지하거나 객체가 많이 생성될 경우에는 규칙을 꼭 지켜야 한다. 안드로이드를 처음 시작하는 많은 개발자가 실수로 흔히 Activity를 여러 곳에서 자유롭게 접근하기 위해 companion 프로퍼티(고전적 형태론 static field)에 이를 할당해 두는 경우가 있다. class MainActivity : AppCompatActivity() { companion object { // 메모리 누수 발생원인 var ..
[Effective Kotlin] Item No.47 인라인 클래스의 사용을 고려하라 하나의 값을 갖는 객체도 inline으로 만들 수 있다. 코틀린 1.3부터 도입된 기능으로, 기본 생성자 프로퍼티가 하나인 클래스 앞에 inline을 붙이면 해당 객체를 사용하는 위치가 모두 해당 프로퍼티로 교체된다. inline class Name(private val value: String) { // ... } 이런 인라인 클래스는 타입만 맞다면 값을 곧바로 집어넣는 것도 허용된다. fun main() { val name = Name("James") // 위 코드는 컴파일 시 아래와 같이 바뀐다 val name: String = "James" } fun main() { val name = Name("James") name.greet() // 안녕, 내 이름은 James야 } inline class ..
[Effective Kotlin] Item No.46 함수 타입 파라미터를 갖는 함수에 inline 한정자를 붙여라 코틀린 표준 라이브러리의 고차 함수를 보면 대부분 inline 한정자가 붙어 있다. inline 한정자의 역할은 컴파일 시점에 '함수를 호출하는 부분'을 '함수의 본문'으로 대체하는 것이다. inline 한정자를 붙여 함수를 만들면 큰 변화가 일어난다. 일반적인 함수를 호출하면 함수 본문으로 점프하고 본문의 모든 문장을 호출한 뒤에 함수를 호출했던 위치로 다시 점프하는 과정을 거친다. 하지만 '함수를 호출하는 부분'을 '함수 본문'으로 대체하면 이런 점프가 일어나지 않는다. inline 한정자를 쓰면 아래의 장점이 있다. 타입 아규먼트에 reified 한정자를 붙여 사용할 수 있다 함수 타입 파라미터를 가진 함수가 훨씬 빠르게 동작한다 비지역(non-local) 리턴을 사용할 수 있다 단점도 존재한다. ..
[Effective Kotlin] Item No.45 불필요한 객체 생성을 피하라 불필요한 객체 생성을 피하는 것은 최적화의 관점에서 좋다. JVM에서는 하나의 가상 머신에서 동일한 문자열을 처리하는 코드가 여러개 있다면, 기존의 문자열을 재사용 Interger와 Long 처럼 박스화한 기본 자료형도 작은 경우에 재사용 Int 의 경우 -128 ~ 127 까지 캐싱 nullable 타입은 int 자료형 대신 Integer 자료형을 사용하게 강제 Int를 사용하면 일반적으로 기본 자료형 int 로 컴파일 하지만 nullable로 만들거나, 타입 아규먼트로 사용할 경우에는 Interger로 컴파일된다. 어떠한 객체를 wrap 할 경우 크게 세 가지의 비용이 발생한다. 객체는 더 많은 용량을 차지 요소가 캡슐화되어 있다면, 접근에 추가적인 함수 호출이 필요 객체는 생성되어야하고, 메모리 영..