JVMコンパイルの場合の話です。
IntArray
と Array<Int>
は似たように使えますが、 似て非なるものです。 IntArray
のほうが、スピードが速いです。 (IntArray
のほかにも CharArray
, BooleanArray
, LongArray
, ShortArray
, ByteArray
が Kotlin には用意されています。)
どれくらい似ているのか
瓜二つです。
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
}
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>
では、 ボクシングをするため オーバヘッドが生じています。
補足: ボクシング
Boxingは Java のプリミティブ型から参照型オブジェクトを作成することをいいます。 Java では int
などの基本型があり、 基本型のラッパーとして参照型 Integer
があります。 参照型としてラップしてオブジェクトにすると、メソッドを使うことができます。 単純なものでは、 int
に toString()
はなく、 Integer
には toString()
があります。 参照型オブジェクトからプリミティブ型の値を取り出すことを Unboxingといいます。
C# でも値型とオブジェクト型があり、ボクシング・アンボクシングがあります。
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
}
変換するには
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要素の配列を作るというのをなんども繰り返して合計でかかった時間を処理時間としています。
var cost = 0L
repeat(10) {
cost += kotlin.system.measureTimeMillis {
repeat(1000000) {
IntArray(100) { 1 }
}
}
}
println(cost)
// => 955
var cost = 0L
repeat(10) {
cost += kotlin.system.measureTimeMillis {
repeat(1000000) {
Array(100) { 1 }
}
}
}
println(cost)
// => 3437
他のデータ
Gist にデータがありました。 Kotlin Merge Sort 。 IntArray
はボクシングとアンボクシングの手間が省かれているため、速いです。 マージソートでの比較では IntArray
を使うと、 Array<Int>
の 1/2以下の時間でソートができているそうです。