Kotlin: IntArray と Array<Int> の違い

JVMコンパイルの場合の話です。

`IntArray` と `Array<Int>` は似たように使えますが、 似て非なるものです。 `IntArray` のほうが、スピードが速いです。 (`IntArray` のほかにも `CharArray`, `BooleanArray`, `LongArray`, `ShortArray`, `ByteArray` が Kotlin には用意されています。)

## どれくらい似ているのか

瓜二つです。

```kotlin:IntArray
public class IntArray(size: Int) {
    public inline constructor(size: Int, init: (Int) -> Int)
    public operator fun get(index: Int): Int
    public operator fun set(index: Int, value: Int): Unit
    public val size: Int
    public operator fun iterator(): IntIterator
}
```

```kotlin:Array
public class Array<T> {
    public inline constructor(size: Int, init: (Int) -> T)
    public operator fun get(index: Int): T
    public operator fun set(index: Int, value: T): Unit
    public val size: Int
    public operator fun iterator(): Iterator<T>
}
```

## どこが違うのか

Kotlin のドキュメントにもありますが、  `IntArray` は Java でいうところの `int[]` を表したもの、 `Array<Int>` は Java の `Integer` の Array です。

逆アセンブル見てみると違いがわかります。 宣言時に、 `INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer` という記述があります。 `Array<Int>` では、 ボクシングをするため オーバヘッドが生じています。 

### 補足: ボクシング

<small><ruby>Boxing<rp>(</rp><rt>ボクシング</rt><rp>)</rp></ruby>は Java のプリミティブ型から参照型オブジェクトを作成することをいいます。 Java では `int` などの基本型があり、 基本型のラッパーとして参照型 `Integer` があります。 参照型としてラップしてオブジェクトにすると、メソッドを使うことができます。 単純なものでは、 `int` に `toString()` はなく、 `Integer` には `toString()` があります。 参照型オブジェクトからプリミティブ型の値を取り出すことを <ruby>Unboxing<rp>(</rp><rt>アンボクシング</rt><rp>)</rp></ruby>といいます。</small>
<small>C# でも値型とオブジェクト型があり、ボクシング・アンボクシングがあります。</small>

```kotlin:Test.kt
val a = intArrayOf(1, 2, 3) // IntArray
val b = arrayOf(1, 2, 3)    // Array<Int>
```

逆アセンブルすると次のようになります。 よく見ると、 `arrayOf` では `java.lang.Integer` のインスタンスを生成して配列にしているのがわかりますね。

```
// ================TestKt.class =================
// class version 50.0 (50)
// access flags 0x31
public final class TestKt {


  // access flags 0x1A
  private final static [I a
  @Lorg/jetbrains/annotations/NotNull;() // invisible

  // access flags 0x19
  public final static getA()[I
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 4 L0
    GETSTATIC TestKt.a : [I
    ARETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x1A
  private final static [Ljava/lang/Integer; b
  @Lorg/jetbrains/annotations/NotNull;() // invisible

  // access flags 0x19
  public final static getB()[Ljava/lang/Integer;
  @Lorg/jetbrains/annotations/NotNull;() // invisible
   L0
    LINENUMBER 5 L0
    GETSTATIC TestKt.b : [Ljava/lang/Integer;
    ARETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x8
  static <clinit>()V
   L0
    LINENUMBER 4 L0
    ICONST_3
    NEWARRAY T_INT
    DUP
    ICONST_0
    ICONST_1
    IASTORE
    DUP
    ICONST_1
    ICONST_2
    IASTORE
    DUP
    ICONST_2
    ICONST_3
    IASTORE
    PUTSTATIC TestKt.a : [I
   L1
    LINENUMBER 5 L1
    ICONST_3
    ANEWARRAY java/lang/Integer
    DUP
    ICONST_0
    ICONST_1
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    AASTORE
    DUP
    ICONST_1
    ICONST_2
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    AASTORE
    DUP
    ICONST_2
    ICONST_3
    INVOKESTATIC java/lang/Integer.valueOf (I)Ljava/lang/Integer;
    AASTORE
    PUTSTATIC TestKt.b : [Ljava/lang/Integer;
    RETURN
    MAXSTACK = 4
    MAXLOCALS = 0

  @Lkotlin/Metadata;(mv={1, 1, 10}, bv={1, 0, 2}, k=2, d1={"\u0000\u0016\n\u0000\n\u0002\u0010\u0015\n\u0002\u0008\u0003\n\u0002\u0010\u0011\n\u0002\u0010\u0008\n\u0002\u0008\u0004\"\u0011\u0010\u0000\u001a\u00020\u0001\u00a2\u0006\u0008\n\u0000\u001a\u0004\u0008\u0002\u0010\u0003\"\u0019\u0010\u0004\u001a\u0008\u0012\u0004\u0012\u00020\u00060\u0005\u00a2\u0006\n\n\u0002\u0010\u0009\u001a\u0004\u0008\u0007\u0010\u0008\u00a8\u0006\n"}, d2={"a", "", "getA", "()[I", "b", "", "", "getB", "()[Ljava/lang/Integer;", "[Ljava/lang/Integer;", "production sources for module test_main"})
  // compiled from: Test.kt
}
```

## 変換するには

```kotlin:Test.kt
val a = intArrayOf(1, 2, 3) // IntArray
val b = arrayOf(1, 2, 3)    // Array<Int>
```

このように定義した2種類の配列を、 `a = b` または `b = a` のようにすることはできません。 `toTypedArray`, `toIntArray` を使います。

```
a = b.toIntArray()
b = a.toTypedArray()
```

## パフォーマンスの差

### やってみた

処理時間の比較は次の通りとなりました。

| | 処理時間 |
|:--|--:|
| `IntArray` | 955|
| `Array` | 3437 |

比較に使ったのは次のコードです。 100要素の配列を作るというのをなんども繰り返して合計でかかった時間を処理時間としています。

```kotlin
var cost = 0L
repeat(10) {
    cost += kotlin.system.measureTimeMillis {
        repeat(1000000) {
            IntArray(100) { 1 }
        }
    }
} 
println(cost)
// => 955
```

```kotlin
var cost = 0L
repeat(10) {
    cost += kotlin.system.measureTimeMillis {
        repeat(1000000) {
            Array(100) { 1 }
        }
    }
} 
println(cost)
// => 3437
```

### 他のデータ

Gist にデータがありました。 [Kotlin Merge Sort](https://gist.github.com/max333/01dc267dd62f713c152f1f16ffc6e2f6) 。 `IntArray` はボクシングとアンボクシングの手間が省かれているため、速いです。 マージソートでの比較では `IntArray` を使うと、 `Array<Int>` の 1/2以下の時間でソートができているそうです。