몰?.루();

코틀린 IntArray와 Array<Int> 차이 본문

프로그래밍/안드로이드, 코틀린

코틀린 IntArray와 Array<Int> 차이

toonraon 2022. 2. 24. 01:48

알고리즘 문제를 코틀린으로 풀고, 다른 사람들의 풀이법을 보다보면 저는 Array<Int>로 정수 배열을 저장하는데 사람들은 IntArray로 정수 배열을 저장하는 경우가 많았습니다. 무슨 차이가 있을까 싶어서 찾아봤는데 한국엔 자료가 잘 없어서 여기저기서 찾은 정보들을 취합해서 정리해보았습니다.

 

결론부터 말하자면

IntArray가 Array<Int>보다 미세하게 더 좋습니다.

 

자바 바이트 코드로 저장될 때가 다릅니다

// 코틀린 코드
fun main() {
    val a = IntArray(5)
    val b = Array<Int>(5) { 0 }
}


// 위 코틀린 코드를 자바로 변환시
public static final void main() {
  int[] a = new int[5];
  Integer[] b = new Integer[5];
}

IntArray는 int[]로 변환되고

Array<Int>는 Integer[]로 변환되는 것을 알 수 있습니다.

 

그럼 int와 Integer의 차이는 뭘까요? (Boxing, Unboxing)

int는 primitive type입니다. 클래스가 아닙니다.

Integer는 클래스입니다. int형의 담당 일진 클래스이므로 int의 Wrapper 클래스라고 불립니다.

(long형의 wrapper 클래스는 Long)

 

Integer 변수에 int를 저장하면 int형을 Integer형으로 변환하여서 저장합니다.

이 변환 과정을 boxing(박싱)이라고 합니다.

박싱을 할 때는 Integer.valueOf() 함수를 이용해서 int형을 Integer형으로 변환합니다.

(참고: https://blog.benelog.net/2229687.html)

 

Integer i = 3; // 프로그래머는 이렇게 쓰겠지만

Integer i = Integer.valueOf(3); // 컴파일러는 이렇게 자동 박싱(auto boxing)합니다.

즉, Array<Int>형으로 선언한 배열에 (굳이 Integer 형으로 넣는 게 아닌 이상) 숫자를 넣을 때마다 Integer.valueOf() 함수를 호출하는 비용이 발생한다는 의미입니다.

 

다만 Integer형을 무조건 배열에 집어넣는 경우엔 Integer 배열인 Array<Int>에 넣는 게 IntArray에 넣는 것보다 더 좋을 겁니다.

int형에 Integer 형을 저장할 때는 unboxing(언박싱)이라고 불리는 형 변환이 일어납니다.
언박싱을 할 때는 Integer.intValue(i)를 호출합니다.

하지만 일반적으로 Integer형을 쓸일은 많지 않습니다.

아마 DB랑 연동하는 부분에서는 NULL 처리 때문에 Integer를 쓰는 게 더 유리하다고 알고 있는데 그런 경우가 아니라면 대부분은 IntArray가 성능면에서 약~간 더 좋을 겁니다.

 

그 외에 초기화 할 때 소소한 차이도 존재합니다.

fun main() {
    val a = IntArray(5)
    val b = Array<Int>(5) { 0 }
}

그리고 초기화 할 때를 보시면 IntArray는 별도로 초기화하는 부분을 작성하지 않습니다.

(5)라는 건 배열 크기를 5로 한다는 것이지 그 어디에도 배열 원소들을 무슨 값으로 초기화할지 정하지 않습니다.

기본적으로 그냥 알아서 0으로 초기화 합니다.

 

물론 IntArray(5, { i -> i * i }) 이렇게 초기화값을 람다식으로 넣어줄 수도 있습니다.
(이 경우엔 [0, 1, 4, 9, 16]으로 초기화 됩니다.)

 

반면 Array<Int>는 무조건 초기화를 해주어야합니다. 위 코드에서 { 0 }이 각 원소를 0으로 초기화하겠다는 의미입니다.

원래는 Array<Int>(5, {0})인데 마지막 인자가 람다식이니까 소괄호 밖으로 빼낸 형태입니다.

 

이때 Array<Int>는 Array<Int>(5) 이런 식으로 크기만 정해줄 수 없습니다. 무조건 초기화를 뭘로 해줄지 정해야합니다.

그리고 이는 자바로 변환했을 때 for문을 하나 더 생성하는 차이가 생깁니다.

 

// 코틀린 코드
fun main() {
    val a = IntArray(5)
    val b = Array<Int>(5) { 0 }
}

// 변환된 자바 코드
public static final void main() {
  int[] a = new int[5];
  Integer[] b = new Integer[5];

  // b 배열 초기화해주는 부분
  for(int var4 = 0; var4 < 5; ++var4) {
     int var6 = false;
     Integer var9 = 0;
     b[var4] = var9;
  }
}

저렇게 for문으로 돌아가며 b배열을 초기화해주는 코드가 자동으로 생성됩니다.

어차피 IntArray도 초기값을 정해주면 결국 for문으로 돌면서 초기화해주는 부분이 생기겠지만 보통 Int형 Array 자체가 0으로 다 초기화하는 경우가 많기 때문에 초기값을 0으로 하고 싶다면 IntArray로 하는 게 굳이 { 0 }라는 초기화 코드를 안 써도 되서 좋고, 성능적으로도 좋습니다.

 

실제 성능 차이

백준에서 15652번 N과 M (4)번 문제를 각각 IntArray와 Array<Int>로 풀이한 모습입니다. 다른 부분 코드는 전부 같습니다.

위쪽에 제출한 게 IntArray를 사용한 코드인데 메모리 상으로도, 시간 상으로도 좀 더 이득을 본 모습을 알 수 있습니다.

시간상 이득은 아무래도 Integer.valueOf() 함수를 호출하지 않아도 되는데에서 온 이득으로 생각되고,메모리 상 이득은 Integer형은 20바이트 크기인데에 반해(Integer 클래스 자체 크기 16바이트 + int 참조 4바이트) int형은 4바이트이므로 거기서 온 차이인 것으로 생각됩니다.

 

 

분명 코틀린으로 시작했는데 사실상 자바에 관한 이야기가 더 많았네요. JVM 위에서 돌아가다보니 어쩔 수 없는 것 같습니다.아무튼 Array<Int>(n) { 0 }은 쓰기도 힘들고(무려 괄호가 3종류나 들어가는...) 메모리, 시간적 측면에서도 비효율적이기 때문에 그냥 IntArray를 자주 쓰게 될 것 같습니다.

Comments